Skip to content

Multi-system-message prompt assembly breaks Fireworks (opaque 500), merge system messages at the model-call boundary #596

Description

@krzkz94

TL;DR

Current output (what reaches the provider when an agent has instructions + one defineInstructions/defineDynamic, and the user message carries a file part):

[
  { "role": "system", "content": "<agent instructions>" },
  { "role": "system", "content": "<dynamic instructions>" },
  {
    "role": "user",
    "content": [
      { "type": "text", "text": "What's my P&L for 2025?" },
      { "type": "file", "filename": "receipt.jpg", "mediaType": "image/jpeg", "...": "..." }
    ]
  }
]
// Fireworks → HTTP 500 (opaque), turn parks with MODEL_CALL_FAILED

Expected output (system messages merged at the model-call boundary; history/lifecycle unchanged):

[
  { "role": "system", "content": "<agent instructions>\n\n<dynamic instructions>" },
  {
    "role": "user",
    "content": [
      { "type": "text", "text": "What's my P&L for 2025?" },
      { "type": "file", "filename": "receipt.jpg", "mediaType": "image/jpeg", "...": "..." }
    ]
  }
]
// Fireworks → 200

What problem are you trying to solve?

eve assembles the model-call instructions as an array of system messages: prepareModelCallInput (harness/tool-loop) builds [agent.system, ...dynamic instruction messages, skill announcements, delivery note]. Any agent that combines defineAgent({ instructions }) with one defineInstructions/defineDynamic therefore sends two or more system-role messages on every call.

Fireworks' OpenAI-compatible chat completions endpoint (observed on accounts/fireworks/models/qwen3p7-plus, eve 0.21.1 via @ai-sdk/fireworks) returns an opaque HTTP 500 when a request combines 2+ system messages with array-form user content. Each half of the pattern works alone: one system message + array user content is fine, multiple system messages + plain string user content is fine. The intersection fails deterministically. We've verified this is a Fireworks server-side bug and reported it to them separately — but eve emits the triggering pattern by design, and nothing about the semantics requires N separate system messages.

This is about to hit more people: eve 0.21's native attachment lane produces array-form user content whenever a message carries file parts, so "agent with dynamic instructions + user sends an attachment" = every turn fails with MODEL_CALL_FAILED on Fireworks-hosted models.

Repro sketch:

  1. defineAgent({ model: fireworks('accounts/fireworks/models/qwen3p7-plus'), instructions }) plus any defineInstructions/defineDynamic (→ 2 system messages).
  2. Send a turn whose user message has array content (e.g. a file part).
  3. Provider returns 500; turn parks with MODEL_CALL_FAILED.

Workaround we're running: a LanguageModelMiddleware whose transformParams concatenates all system messages into one (join('\n\n')). No behavioral difference observed — which supports merging being semantically safe.

Proposed solution

Merge the assembled system messages into a single system message at the model-call boundary (prepareModelCallInput), keeping them separate in session history and in the dynamic-instruction lifecycle. For OpenAI-compatible APIs the concatenation is semantically equivalent; Anthropic handles system separately anyway. The applySystemCacheBreakpoint logic would apply to the merged message. If unconditional merging feels too opinionated, an agent-level opt-in (e.g. mergeSystemMessages: true) would do — but defensive-by-default seems right given providers demonstrably choke on the pattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions