{"record":{"id":"d6581deb61e1dd49","repo":"odysseus-dev/odysseus","slug":"session-request-returned-an-invalid-response","errorCode":null,"errorMessage":"Session request returned an invalid response","messagePattern":"Session request returned an invalid response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/sessions.js","lineNumber":1699,"sourceCode":"      let url = `${API_BASE}/api/sessions`;\n      if (currentSessionId && _isIncognitoSession(currentSessionId)) {\n        url += `?active_incognito_id=${encodeURIComponent(currentSessionId)}`;\n      }\n      const res = await fetch(url);\n      if (!res.ok) {\n        let detail = '';\n        try {\n          const payload = await res.json();\n          detail = payload?.detail || payload?.error || '';\n        } catch (_) {}\n        const error = new Error(detail || `Session request failed (HTTP ${res.status})`);\n        error.status = res.status;\n        throw error;\n      }\n      fetched = await res.json();\n    }\n    if (!Array.isArray(fetched)) {\n      throw new Error('Session request returned an invalid response');\n    }\n    sessions = _normalizeSessionsList(fetched);\n    renderSessionList();\n\n    const sessionsSection = uiModule.el('sessions-section');\n    if (sessions.length === 0) {\n      sessionsSection.classList.add('hidden');\n    } else {\n      sessionsSection.classList.remove('hidden');\n    }\n\n    const activeSessions = sessions.filter(s => !s.archived);\n    // \"Transient\" sessions = the singleton Assistant chat + any task-output\n    // session. Treat them as not-restorable so coming back to the app lands\n    // on the user's last actual conversation, not whichever check-in task\n    // most recently appended a message.\n    const _isTransient = (s) => !!s && (s.folder === 'Assistant' || s.folder === 'Tasks');\n    const _realSessions = activeSessions.filter(s => !_isTransient(s));","sourceCodeStart":1681,"sourceCodeEnd":1717,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/sessions.js#L1681-L1717","documentation":"Thrown by loadSessions in sessions.js when the sessions request succeeded (or was cached) but res.json() did not yield an array. It is distinct from the HTTP-status error just above it ('Session request failed (HTTP n)'), which carries .status — this one means the payload shape is wrong despite a 2xx.","triggerScenarios":"The sessions endpoint returning a wrapped object like {sessions:[...]} instead of a bare array; a 200 HTML page from a misrouted proxy or the SPA index fallback; a server version change to a paginated envelope {items, total}; JSON parsing of an empty body producing undefined.","commonSituations":"Frontend/backend version skew after an API envelope refactor; catch-all SPA routing serving index.html for /api/session on misconfigured hosts; login pages returned in place of JSON when auth middleware redirects.","solutions":["Inspect the network response for /api/session* — compare its top-level shape with Array.isArray expectation.","If wrapped, unwrap in the client (see exampleFix) or revert the server to a bare array.","Fix proxy/SPA fallback config so API routes are not served index.html.","Ensure auth redirects return JSON errors, not HTML."],"exampleFix":"// before\nfetched = await res.json();\nif (!Array.isArray(fetched)) { throw new Error('Session request returned an invalid response'); }\n\n// after (accept wrapped envelope)\nfetched = await res.json();\nconst list = Array.isArray(fetched) ? fetched : fetched?.sessions ?? fetched?.items;\nif (!Array.isArray(list)) { throw new Error('Session request returned an invalid response'); }\nsessions = _normalizeSessionsList(list);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"const isSessionList = (v) => Array.isArray(v) || Array.isArray(v?.sessions) || Array.isArray(v?.items);","tryCatchPattern":"try { fetched = await res.json(); const list = Array.isArray(fetched) ? fetched : fetched?.sessions ?? fetched?.items; if (!Array.isArray(list)) throw new Error('Session request returned an invalid response'); } catch (e) { showError(e.message); renderSessionList(); /* render cached/empty rather than blank */ }","preventionTips":["Keep the sessions endpoint returning a bare array, or version the envelope when changing it","Configure proxies so /api/* is never served the SPA index.html","Type-check the parsed shape before mutating global session state"],"tags":["sessions","api-shape","contract-violation","proxy"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}