{"record":{"id":"40b60e6eba88d68d","repo":"odysseus-dev/odysseus","slug":"document-create-failed-missing-id","errorCode":null,"errorMessage":"Document create failed: missing id","messagePattern":"Document create failed: missing id","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/document.js","lineNumber":7046,"sourceCode":"    // If the panel was in empty-state, the user may type into the editor\n    // during the create round-trip — preserve that text into the new doc\n    // instead of letting switchToDoc blank it.\n    const wasEmpty = !activeDocId;\n    try {\n      const res = await fetch(`${API_BASE}/api/document`, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        credentials: 'same-origin',\n        body: JSON.stringify({\n          session_id: sessionId,\n          title: '',\n          content: '',\n          language: 'markdown',\n        }),\n      });\n      if (!res.ok) throw new Error(`Document create failed: HTTP ${res.status}`);\n      const doc = await res.json();\n      if (!doc || !doc.id) throw new Error('Document create failed: missing id');\n      addDocToTabs(doc, sessionId);\n      if (!isOpen) openPanel();\n      // Re-enable editor if it was in empty state\n      let textarea = document.getElementById('doc-editor-textarea');\n      if (textarea) {\n        textarea.disabled = false;\n        textarea.placeholder = 'Document content...';\n      }\n      // Capture text typed during the round-trip (only when starting from the\n      // empty editor — don't steal another doc's content).\n      const typed = (wasEmpty && textarea && textarea.value.trim()) ? textarea.value : '';\n      switchToDoc(doc.id);\n      if (typed) {\n        textarea = document.getElementById('doc-editor-textarea');\n        if (textarea) textarea.value = typed;\n        const d = docs.get(doc.id);\n        if (d) d.content = typed;\n        syncHighlighting();","sourceCodeStart":7028,"sourceCodeEnd":7064,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/document.js#L7028-L7064","documentation":"Thrown in static/js/document.js when POST /api/document returns 2xx but the JSON body has no id (falsy doc or doc.id). It guards addDocToTabs/switchToDoc from receiving an unusable document record — a response-shape contract violation rather than an HTTP failure.","triggerScenarios":"Backend returns {ok: true} or an empty object instead of the created document; a proxy or interceptor rewrites the response; backend refactor renamed id to doc_id (note the sibling import path at document.js:9584 accepts both j.id || j.doc_id, so this site is stricter).","commonSituations":"API version drift where the create endpoint's response schema changed; middleware returning 200 with a wrapper object {data: {...}}.","solutions":["Inspect the actual 200 response body for POST /api/document.","If the field was renamed, accept the alias here like the import path does (doc.id || doc.doc_id) or fix the backend to return id.","Ensure no response-transforming middleware wraps the payload."],"exampleFix":"// before\nconst doc = await res.json();\nif (!doc || !doc.id) throw new Error('Document create failed: missing id');\n\n// after\nconst doc = await res.json();\nconst newId = doc && (doc.id || doc.doc_id);\nif (!newId) throw new Error(`Document create failed: missing id (keys: ${Object.keys(doc || {}).join(', ')})`);\ndoc.id = newId;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function hasDocumentId(doc) {\n  return doc != null && typeof doc === 'object'\n    && (typeof doc.id === 'string' || typeof doc.id === 'number')\n    && String(doc.id).length > 0;\n}","tryCatchPattern":"const doc = await res.json();\nif (!hasDocumentId(doc) && !doc?.doc_id) {\n  throw new Error(`Document create failed: missing id (got keys: ${Object.keys(doc || {}).join(', ')})`);\n}","preventionTips":["Accept the id/doc_id alias like the import path does, or normalize the backend to always return id.","Contract-test the create endpoint's 200 body shape in CI.","Watch for middleware that wraps responses in {data: ...}."],"tags":["api-contract","response-shape","document-create","validation"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}