{"record":{"id":"f1343af49aae43cc","repo":"router-for-me/CLIProxyAPI","slug":"codex-live-call-request-requires-an-sdp-offer","errorCode":null,"errorMessage":"Codex live call request requires an SDP offer","messagePattern":"Codex live call request requires an SDP offer","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"internal/client/codex/live/live.go","lineNumber":691,"sourceCode":"\tpayload := struct {\n\t\tSDP     string          `json:\"sdp\"`\n\t\tSession json.RawMessage `json:\"session,omitempty\"`\n\t}{\n\t\tSDP:     sdp,\n\t\tSession: session,\n\t}\n\tencoded, errMarshal := json.Marshal(payload)\n\tif errMarshal != nil {\n\t\treturn nil, fmt.Errorf(\"failed to encode Codex live request: %w\", errMarshal)\n\t}\n\treturn encoded, nil\n}\n\nfunc callRequestSDP(body []byte, contentType string) (string, error) {\n\tmediaType, _, errMediaType := mime.ParseMediaType(contentType)\n\tif errMediaType == nil && (strings.EqualFold(mediaType, \"application/sdp\") || strings.EqualFold(mediaType, \"text/plain\")) {\n\t\tif strings.TrimSpace(string(body)) == \"\" {\n\t\t\treturn \"\", errors.New(\"Codex live call request requires an SDP offer\")\n\t\t}\n\t\treturn string(body), nil\n\t}\n\tif errMediaType != nil || !strings.EqualFold(mediaType, \"application/json\") {\n\t\treturn \"\", errors.New(\"Codex live media relay requires an SDP or JSON call request\")\n\t}\n\tvar payload struct {\n\t\tSDP string `json:\"sdp\"`\n\t}\n\tif errUnmarshal := json.Unmarshal(body, &payload); errUnmarshal != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode Codex live call request: %w\", errUnmarshal)\n\t}\n\tif strings.TrimSpace(payload.SDP) == \"\" {\n\t\treturn \"\", errors.New(\"Codex live call request requires an SDP offer\")\n\t}\n\treturn payload.SDP, nil\n}\n","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/live.go#L673-L709","documentation":"The HTTP stream bridge found no entry for the given id: either no stream was ever opened with it, or the stream was already closed and removed. Reads on unknown/expired IDs return this error (with done=true) instead of blocking.","triggerScenarios":"Calling read after close; reading an ID from a previous host process (the bridge is in-memory); double-closing then reading; using an ID after the stream ended naturally (bridge auto-closes on channel close or ctx cancellation).","commonSituations":"Plugin retry logic that re-reads after a completed stream; host restart invalidating all IDs while the plugin keeps state; races between a consumer goroutine finishing the stream and another goroutine issuing one more read.","solutions":["Stop reading once a read returns Done=true or an error; treat unknown-stream as terminal, not retryable.","Close over the read loop so only one goroutine consumes a given StreamID.","Drop all stream IDs on host reconnection/restart and re-open streams as needed."],"exampleFix":"// after: treat 'not open' as terminal\nchunk, done, err := read(ctx, streamID)\nif err != nil {\n    if strings.Contains(err.Error(), \"is not open\") {\n        return nil // stream already finished/elsewhere closed; stop cleanly\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"chunk, done, err := bridge.Read(ctx, id)\nif err != nil {\n    if strings.Contains(err.Error(), \"is not open\") {\n        return nil // stream already closed/finished elsewhere — treat as terminal\n    }\n    if errors.Is(err, context.Canceled) {\n        return err\n    }\n    return err\n}\nif done { break }","preventionTips":["Stop reading on Done=true or any error; never re-read a closed stream.","Single consumer per stream ID.","Invalidate all stream IDs after host restarts since the bridge is in-memory."],"tags":["pluginhost","streaming","http","lifecycle"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}