#17888: fix: normalize /v1/responses input for clients that omit type field
gateway
size: XS
## Summary
Fixes `400 Bad Request` ("input: Invalid input") when OpenAI SDK clients send
`/v1/responses` input messages without the `type: "message"` discriminator field.
- n8n (v2.2.4+), and potentially other clients, send `{"role":"user","content":"hello"}`
without `type` — OpenAI's API accepts this, but the Zod `discriminatedUnion("type", [...])`
schema rejects it
- Adds a `normalizeResponseInput()` pre-processing step before Zod validation
- Also normalizes `type: "text"` content parts to `type: "input_text"` for clients that
use the Chat Completions content format
- Changes `CreateResponseBodySchema` from `.strict()` to `.strip()` so unknown top-level
fields (e.g. `parallel_tool_calls`, `background`) forwarded by clients are silently
discarded instead of causing validation errors
## Changes
**`src/gateway/openresponses-http.ts`:**
- Adds `normalizeResponseInput()` function (~25 lines)
- Wraps the existing `CreateResponseBodySchema.safeParse()` call with normalization
**`src/gateway/open-responses.schema.ts`:**
- Changes `.strict()` to `.strip()` on `CreateResponseBodySchema`
## Normalization rules
| Client sends | Normalized to | Why |
|---|---|---|
| `{"role":"user","content":"..."}` (no `type`) | `{"type":"message","role":"user","content":"..."}` | OpenAI infers `type: "message"` when `role` is present |
| `{"type":"text","text":"..."}` in content array | `{"type":"input_text","text":"..."}` | Some clients use Chat Completions content format |
## Test Plan
- [x] `POST /v1/responses` with `[{"role":"user","content":"hello"}]` (no type) — was 400, now accepted
- [x] `POST /v1/responses` with `[{"type":"message","role":"user","content":[{"type":"text","text":"hello"}]}]` — was 400, now accepted
- [x] `POST /v1/responses` with correct format still works
- [x] `POST /v1/responses` with string input still works
- [x] Tested with n8n v2.2.4 OpenAI Chat Model node (Responses API mode)
- [x] `pnpm build` passes with zero errors
- [x] Rebased onto current main
## AI Disclosure 🤖
- **AI-assisted**: Yes — written with Claude (Opus), with human review and testing
- **Testing level**: Build-verified (`pnpm build` zero errors); endpoint tested with n8n v2.2.4 OpenAI Chat Model node
- **Understanding**: The author understands the code — it pre-processes the raw request body to infer `type: "message"` for items that have `role` but no `type`, matching OpenAI's own inference behavior, and rewrites `type: "text"` content parts to the `input_text` variant expected by the Zod schema
Most Similar PRs
#20878: fix: Widen models.input to accept "video" and "audio" modalities
by marcodelpin · 2026-02-19
67.1%
#21298: fix(config): extend model input schema for video/audio modalities
by Alfa-ai-ccvs-tech · 2026-02-19
66.1%
#7392: OpenResponses: tool output items, reasoning summary, opt‑in tool da...
by lylepratt · 2026-02-02
62.9%
#23507: fix(tools): strip patternProperties from schemas for OpenAI-compati...
by long-pham · 2026-02-22
61.9%
#20867: fix: allow 'video' and 'audio' in models.input config
by pierreeurope · 2026-02-19
61.4%
#10943: fix(config): resolve Control UI "Unsupported schema node" for confi...
by kraftbj · 2026-02-07
61.2%
#20738: Fix model input schema to accept audio and video modalities
by Clawborn · 2026-02-19
60.7%
#21499: fix #20721: add video and audio to models.input type union
by neipor · 2026-02-20
60.2%
#12642: feat(tools): typed tool schemas for xAI/Grok compatibility
by 2nd-ren · 2026-02-09
60.0%
#19394: fix(agents): normalize tool call arguments dropped to {} (#19261)
by DevvGwardo · 2026-02-17
59.1%