{"record":{"id":"93ecf1d99a20c7bf","repo":"siyuan-note/siyuan","slug":"response-stream-terminal-event-is-missing-response","errorCode":null,"errorMessage":"response stream terminal event is missing response","messagePattern":"response stream terminal event is missing response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/openai_completion.go","lineNumber":469,"sourceCode":"\t\t\tcall := stream.responseToolCalls[index]\n\t\t\tcall.Index = &index\n\t\t\tcall.Type = openai.ToolTypeFunction\n\t\t\tcall.Function.Arguments += event.Delta\n\t\t\tstream.responseToolCalls[index] = call\n\t\t\tresponse.Choices = []openai.ChatCompletionStreamChoice{{\n\t\t\t\tIndex: 0,\n\t\t\t\tDelta: openai.ChatCompletionStreamChoiceDelta{ToolCalls: []openai.ToolCall{{\n\t\t\t\t\tIndex: &index,\n\t\t\t\t\tType:  openai.ToolTypeFunction,\n\t\t\t\t\tFunction: openai.FunctionCall{\n\t\t\t\t\t\tArguments: event.Delta,\n\t\t\t\t\t},\n\t\t\t\t}}},\n\t\t\t}}\n\t\t\treturn response, nil\n\t\tcase openai.ResponseStreamEventCompleted, openai.ResponseStreamEventIncomplete:\n\t\t\tif event.Response == nil {\n\t\t\t\treturn openai.ChatCompletionStreamResponse{}, errors.New(\"response stream terminal event is missing response\")\n\t\t\t}\n\t\t\tif err = stream.queueResponseTerminal(*event.Response); err != nil {\n\t\t\t\treturn openai.ChatCompletionStreamResponse{}, err\n\t\t\t}\n\t\t\tstream.responsesDone = true\n\t\t\tresponse = stream.pending[0]\n\t\t\tstream.pending = stream.pending[1:]\n\t\t\treturn response, nil\n\t\tcase openai.ResponseStreamEventFailed:\n\t\t\treturn openai.ChatCompletionStreamResponse{}, responseEventError(event, \"response failed\")\n\t\tcase openai.ResponseStreamEventError:\n\t\t\treturn openai.ChatCompletionStreamResponse{}, responseEventError(event, \"response stream failed\")\n\t\t}\n\t\treturn response, nil\n\t}\n}\n\nfunc (stream *OpenAICompletionStream) queueResponseTerminal(response openai.CreateResponseResponse) error {","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/util/openai_completion.go#L451-L487","documentation":"On response.completed / response.incomplete the adapter requires event.Response to carry the full final Response object so it can reconcile deltas, tool calls, usage, and emit the closing chunk. A terminal event without a payload leaves the final state unknowable, so the adapter fails the stream rather than guess.","triggerScenarios":"An OpenAI-compatible provider or proxy emits response.completed with the response field omitted or null; SDK deserialization that drops the field; event rewriters or transforms between gateway and client stripping the payload.","commonSituations":"Switching the AI provider to self-hosted gateways (older vLLM/LiteLLM/one-api builds) with partial Responses-API support; version skew between the gateway and the go-openai SDK.","solutions":["Reproduce with the official OpenAI endpoint; if it only fails on your gateway, the gateway drops the terminal payload — upgrade or fix it.","Capture the raw SSE frames (temporary logging) to confirm response.completed lacks the response object.","Switch that provider to the Chat Completions protocol (\"openai\") instead of \"openai-responses\".","Retry once — some providers send a well-formed terminal event on a second attempt."],"exampleFix":"// before\n{\"apiBase\": \"https://my-gateway/v1\", \"protocol\": \"openai-responses\"}\n\n// after: compliant endpoint for the responses protocol, or chat completions on the gateway\n{\"apiBase\": \"https://api.openai.com/v1\", \"protocol\": \"openai-responses\"}\n{\"apiBase\": \"https://my-gateway/v1\", \"protocol\": \"openai\"}","handlingStrategy":"fallback","validationCode":"// smoke-test a provider before enabling the responses protocol for it\nprobe, err := client.CreateResponseStream(ctx, openai.CreateResponseRequest{Model: model, Input: \"ping\"})\nif err == nil {\n\tfor {\n\t\tev, evErr := probe.Recv()\n\t\tif evErr != nil {\n\t\t\tbreak\n\t\t}\n\t\tif ev.Type == openai.ResponseStreamEventCompleted && ev.Response == nil {\n\t\t\treturn errors.New(\"provider terminal events lack the response payload\")\n\t\t}\n\t}\n\tprobe.Close()\n}","typeGuard":null,"tryCatchPattern":"On this error, mark the provider config as responses-incompatible and route subsequent calls through the Chat Completions protocol or non-streaming CreateResponse; do not retry-loop a deterministic protocol gap.","preventionTips":["Validate third-party endpoints against the Responses API event schema before rollout","Pin gateway versions you have actually tested","Keep per-provider protocol selection configurable"],"tags":["streaming","openai","responses-api","protocol-compliance","sse"],"backgroundTag":"malformed-event-stream","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}