Skip to content

Guest chat requests with malformed messages fail as stream errors instead of 400 #1046

Description

@miurla

Problem

The guest path of POST /api/chat passes the client-supplied messages array to the stream pipeline without checking the shape of its elements. When a client sends a malformed element, the turn throws a TypeError during message preparation, before any model call, and the request is recorded as a failed turn (root research span at ERROR, statusMessage "unknown: We could not generate a response. Please try again.") instead of being rejected as a bad request.

The web UI always sends well-formed messages, so this is only reachable from non-UI clients. The impact is that malformed input is reported as a server-side stream failure, which mixes it into the same error class that real turn failures use.

Evidence

  • Failing spans carry streamErrorPhase: preparation, streamErrorStage: transform-messages, and streamErrorShape.name: TypeError, with no child observations and near-zero latency. The same shape has recurred on separate days, only on guest requests, with no chatId and no user message input.
  • transform-messages is the initial stage on the guest path (lib/streaming/create-ephemeral-chat-stream-response.ts), so the throw happens inside the preparation block: assignDataPartNonces -> stripSpecFromMessages -> compactHistoricalMessages / capHistoricalAttachments -> dedupeAttachments -> summarizeCarriedContext.
  • app/api/chat/route.ts only checks Array.isArray(messages). createEphemeralChatStreamResponse only rejects an empty array. Nothing checks that each element is an object with a string role and an array parts of objects with a string type. For example, assignDataPartNonces (lib/streaming/helpers/data-part-nonce.ts) reads message.parts?.some(part => ... part.type), which throws on a null message, a non-array parts, or a null part.

To reproduce locally as a guest: send POST /api/chat with messages: [null] or messages: [{ "role": "user", "parts": "hello" }].

Proposed direction

Validate the guest messages payload at the route boundary (or at the top of createEphemeralChatStreamResponse) and return 400 Bad Request for elements that are not UI messages with an array of typed parts. Keep the check structural and small; it only needs to guarantee the invariants the preparation helpers already assume.

Acceptance criteria

  • A guest request with a malformed messages element returns 400 and does not create an ERROR root span.
  • Well-formed guest requests are unaffected.
  • A unit test covers the rejected shapes above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    automatedCreated by the automated daily pipeline

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions