{"record":{"id":"130385bceeaedd27","repo":"odysseus-dev/odysseus","slug":"server-returned-no-session-id","errorCode":null,"errorMessage":"Server returned no session id","messagePattern":"Server returned no session id","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/research/panel.js:1239","lineNumber":1239,"sourceCode":"    const payload = await res.json();\n    if (_sessionModule && _sessionModule.selectSession && payload.session_id) {\n      if (_sessionModule.loadSessions) await _sessionModule.loadSessions().catch(() => {});\n      await _sessionModule.selectSession(payload.session_id);\n      closePanel();\n    } else if (payload.session_id) {\n      window.location.hash = '#' + payload.session_id;\n      window.location.reload();\n    } else {\n      // 200 OK but no session_id — server contract violation. Don't leave\n      // the button stuck on 'Creating…'; surface the failure instead.\n      throw new Error('Server returned no session id');\n    }","sourceCodeStart":null,"sourceCodeEnd":null,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/panel.js#L1239","documentation":"Thrown in the same _chatAboutResearch flow (research/panel.js:1239) when the spinoff endpoint returns 200 OK but the JSON payload has no session_id. Per the inline comment this is a server contract violation: success status without the field needed to route the user to the new chat. The button is un-stuck and the failure alerted rather than silently ignored.","triggerScenarios":"Backend version that returns {chat_id} instead of {session_id}; a proxy or middleware stripping/rewriting the response body; server short-circuits with an empty object on partial creation (session created but id serialization failed).","commonSituations":"Client/server version skew after renaming the response field; mock servers returning incomplete payloads; serialization bugs where the ORM id is not included.","solutions":["Inspect the actual 200 response body in the network tab to see which field holds the id.","Update the client to read the current field name, or the server to emit session_id.","Ensure client and server builds are from the same release.","Fix test/mocks to include session_id."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"const isSpinoffPayload = (p) => !!p && (typeof p.session_id === 'string' || typeof p.session_id === 'number');","tryCatchPattern":"try { const payload = await res.json(); if (!isSpinoffPayload(payload)) throw new Error('Server returned no session id'); } catch (e) { btn.disabled = false; btn.innerHTML = origLabel; alert('Could not start follow-up chat: ' + e.message); }","preventionTips":["Never return 200 without the contract fields — use 502/500 for partial failures","Validate response shape before navigating","Add integration tests asserting session_id presence"],"tags":["research","contract-violation","api-shape","session"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}