{"record":{"id":"bcda9755c59687ea","repo":"router-for-me/CLIProxyAPI","slug":"previous-response-not-found","errorCode":"previous_response_not_found","errorMessage":"{\"error\":{\"message\":\"Previous response is not available on this websocket; resend the full conversation input without previous_response_id\",\"type\":\"invalid_request_error\",\"code\":\"previous_response_not_found\",\"param\":\"previous_response_id\"}}","messagePattern":"\\{\"error\":\\{\"message\":\"Previous response is not available on this websocket; resend the full conversation input without previous_response_id\",\"type\":\"invalid_request_error\",\"code\":\"previous_response_not_found\",\"param\":\"previous_response_id\"\\}\\}","errorType":"http","errorClass":null,"httpStatus":409,"severity":"error","filePath":"sdk/api/handlers/openai/openai_responses_websocket.go","lineNumber":700,"sourceCode":"\nfunc websocketUpgradeHeaders(req *http.Request) http.Header {\n\theaders := http.Header{}\n\tif req == nil {\n\t\treturn headers\n\t}\n\n\t// Keep the same sticky turn-state across reconnects when provided by the client.\n\tturnState := strings.TrimSpace(req.Header.Get(wsTurnStateHeader))\n\tif turnState != \"\" {\n\t\theaders.Set(wsTurnStateHeader, turnState)\n\t}\n\treturn headers\n}\n\nfunc responsesWebsocketPreviousResponseNotFoundError() *interfaces.ErrorMessage {\n\treturn &interfaces.ErrorMessage{\n\t\tStatusCode: http.StatusConflict,\n\t\tError: errors.New(\n\t\t\t`{\"error\":{\"message\":\"Previous response is not available on this websocket; resend the full conversation input without previous_response_id\",\"type\":\"invalid_request_error\",\"code\":\"previous_response_not_found\",\"param\":\"previous_response_id\"}}`,\n\t\t),\n\t}\n}\n","sourceCodeStart":682,"sourceCodeEnd":705,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/sdk/api/handlers/openai/openai_responses_websocket.go#L682-L705","documentation":"Returned when a client sends a `response.create` websocket frame carrying a `previous_response_id` while the proxy has no stored turn state for that websocket session (`len(lastRequest) == 0` at sdk/api/handlers/openai/openai_responses_websocket.go:488). The proxy only supports incremental input when it has replayed/recorded the prior turn itself; a bare previous_response_id from a different or already-forgotten session cannot be resolved. It replies HTTP 409-style ErrorMessage with code `previous_response_not_found` instead of forwarding an invalid continuation upstream.","triggerScenarios":"Sending `{\"type\":\"response.create\",\"previous_response_id\":\"resp-...\",\"input\":[...]}` as the FIRST request on a new websocket connection (no prior response.create on this socket); reconnecting with a stale response id but no `X-Turn-State` (wsTurnStateHeader) header/turn-state token; using previous_response_id when native websocket passthrough is disabled and no lastResponseID exists on the session.","commonSituations":"Client reconnects after a dropped websocket and reuses the old response id; client mixes the HTTP Responses API pattern (stateless previous_response_id) with the proxy's websocket endpoint; turn-state header was not persisted across reconnects so sticky state was lost; switching between auth accounts pins a different session with no history.","solutions":["Resend the full conversation input (all messages) with previous_response_id removed on the first request of the websocket session.","On reconnect, echo back the turn-state header value the server gave you (wsTurnStateHeader) so the sticky turn state is restored.","Only send previous_response_id on the second and later response.create frames of the same websocket session.","If you need cross-session continuation, use the HTTP Responses endpoint instead, or enable native websocket passthrough mode."],"exampleFix":"// before (first frame on a new websocket)\nws.Send(`{\"type\":\"response.create\",\"previous_response_id\":\"resp-old\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":\"hi\"}]}`)\n\n// after (full input, no previous_response_id)\nws.Send(`{\"type\":\"response.create\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":\"hi\"}]}`)","handlingStrategy":"validation","validationCode":"// Go client: before sending response.create on a websocket, check session state\nfunc canUsePreviousResponseID(hasPriorTurn bool, req []byte) bool {\n    if !hasPriorTurn {\n        return gjson.GetBytes(req, \"previous_response_id\").String() == \"\"\n    }\n    return true\n}\n// hasPriorTurn = at least one successful response.create completed on THIS connection","typeGuard":"func isFirstTurnOnSocket(lastResponseID string) bool { return strings.TrimSpace(lastResponseID) == \"\" }","tryCatchPattern":"if respErr != nil && strings.Contains(respErr.Error(), \"previous_response_not_found\") {\n    // resend full conversation without previous_response_id\n    delete(payload, \"previous_response_id\")\n    payload[\"input\"] = fullConversationInput\n    resend(payload)\n}","preventionTips":["Track locally whether the current websocket has completed at least one response.create; only then use previous_response_id.","Persist and re-send the turn-state header on reconnect to restore sticky state.","Keep the full conversation history client-side so any socket can be restarted from scratch."],"tags":["websocket","openai-responses","session-state","invalid-request"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}