#16021·ai

OpenAI Responses API: in-stream `server_error` triggered by zod v4 string-format regex `pattern`s in tool schemas (gpt-5.4)

Author: fvaldes33Created Jun 11, 2026Updated Sep 17, 2026
Labelstask-identify-issue-type-donetask-bug-reproduction-cannot-reproducetask-bug-reproduction-v6.0-cannot-reproduce

Description

Calling streamText with the OpenAI Responses API (gpt-5.4) and tools whose zod v4 schemas use string-format validators (z.email(), z.uuid(), z.iso.date()) can make OpenAI fail with an in-stream server_error immediately after response.in_progress:

json
{
  "type": "error",
  "error": {
    "type": "server_error",
    "code": "server_error",
    "message": "An error occurred while processing your request. You can retry your request, or contact us through our help center at help.openai.com...",
    "param": null
  },
  "sequence_number": 2
}

The trigger is the regex pattern keywords that zod v4's toJSONSchema() emits for these validators (and which the AI SDK forwards to the provider verbatim):

jsonc
// z.email() — contains negative lookaheads, which OpenAI's structured-output
// docs list as unsupported regex features
"email": {
  "type": "string",
  "format": "email",
  "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
}

// z.iso.date() — large leap-year-aware alternation
"birthDate": {
  "type": "string",
  "format": "date",
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
}

// z.uuid()
"kidId": {
  "type": "string",
  "format": "uuid",
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$"
}

Evidence (replaying the captured /v1/responses body directly against api.openai.com)

Variant Result
Captured body (12 tools, ~3.5k-token system prompt, 5-message history) 24/24 in-stream server_error over several hours
Identical body, only pattern keywords deleted from tool schemas 0 failures
Identical body, pattern deleted but format kept 0 failures (format alone is fine)
Small perturbations (different last user message, one tool dropped, random prefix added) flaky, ~40–70% failure
Each suspect schema in isolation (1 tool, short prompt) passes

So the patterns are necessary but not sufficient — failure probability scales with aggregate prompt/schema complexity, and for some real conversations it reaches 100%. In production this presents as "one conversation thread permanently broken while others work," which is nasty to diagnose because the error is a generic 500 with no param pointer.

The failure arrives ~0.7–1.2s into the stream, before any output item, suggesting OpenAI dies preparing constrained decoding/grammar state rather than during sampling. (The Responses API now defaults function tools to strict: true.)

I could not produce a shareable always-failing repro — the failing manifold is token-sensitive, and anonymizing the conversation content changes the failure rate (my fully genericized rewrite passes consistently). I'm happy to share the exact failing body privately. Failing OpenAI request IDs that OpenAI can inspect server-side:

req_e2157565c8594b388f30971ed6b0b6ab, req_ec60e77c95da4f46b20c397e9758d9ae, req_5f484d89e2d7498f89a6806f8f59b85d, req_496dd2490bea4171a06605a0bcb6ce4d, req_bdee906f91394404b36c4b4becb1d4a9, req_c93d169ddb654e8ebc46d641150470f5, req_427dac0fb0d3425687fe1068e2b737c6, req_e667f935118a4d2386dba73e3cb6ddd6

This is arguably an OpenAI backend bug — why file here?

  1. The AI SDK's default zod v4 conversion is what puts these regexes (including lookaheads OpenAI documents as unsupported) into every tool schema, so AI SDK users hit this without ever writing a pattern themselves. The provider could sanitize pattern (or at least strip known-unsupported regex features) for OpenAI, the way other provider-specific schema fixes are handled.
  2. Failing that, a troubleshooting docs entry would help — this is a close cousin of the existing ".optional()/.nullish() break OpenAI structured outputs" page, but it presents as a retryable-looking 500 instead of a clean error, so people burn a lot of time before suspecting their schemas.

Workaround

Provider-level middleware that strips pattern keywords before the request, while zod still validates tool inputs at runtime:

typescript
import { wrapLanguageModel, type LanguageModelMiddleware } from "ai";

const stripJsonSchemaPatternsMiddleware: LanguageModelMiddleware = {
  specificationVersion: "v3",
  transformParams: async ({ params }) => ({
    ...params,
    tools: params.tools?.map(tool =>
      tool.type === "function"
        ? { ...tool, inputSchema: stripPatternKeywordDeep(tool.inputSchema) }
        : tool
    ),
    responseFormat:
      params.responseFormat?.type === "json" && params.responseFormat.schema
        ? {
            ...params.responseFormat,
            schema: stripPatternKeywordDeep(params.responseFormat.schema),
          }
        : params.responseFormat,
  }),
};

function stripPatternKeywordDeep<T>(schema: T): T {
  const visit = (node: unknown): unknown => {
    if (Array.isArray(node)) return node.map(visit);
    if (node === null || typeof node !== "object") return node;
    const out: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(node)) {
      if (key === "pattern" && typeof value === "string") continue;
      // keys of these maps are property names, not schema keywords
      if (
        ["properties", "patternProperties", "$defs", "definitions"].includes(key) &&
        value && typeof value === "object" && !Array.isArray(value)
      ) {
        out[key] = Object.fromEntries(
          Object.entries(value).map(([k, v]) => [k, visit(v)])
        );
        continue;
      }
      out[key] = visit(value);
    }
    return out;
  };
  return visit(schema) as T;
}

const model = wrapLanguageModel({
  model: openai("gpt-5.4"),
  middleware: stripJsonSchemaPatternsMiddleware,
});

With this middleware the previously 100%-failing conversation streams fine (verified repeatedly).

AI SDK Version

  • ai: 6.0.39
  • @ai-sdk/openai: 3.0.12
  • zod: 3.25.76 (schemas authored via import { z } from "zod/v4")
  • Model: gpt-5.4 (Responses API, streaming), reproduced both via a gateway (Portkey) and directly against api.openai.com