{"record":{"id":"42ca0b546d5c4e3a","repo":"odysseus-dev/odysseus","slug":"statustext-42ca0b","errorCode":null,"errorMessage":"statusText","messagePattern":"statusText","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/editor/ai-inpaint.js","lineNumber":154,"sourceCode":"      const dilatedMask = dilateMask(mergedMask, padPx);\n      const imageB64 = flatCanvas.toDataURL('image/png').split(',')[1];\n      const maskB64 = dilatedMask.toDataURL('image/png').split(',')[1];\n      const baseSnap = document.createElement('canvas');\n      baseSnap.width = state.imgWidth;\n      baseSnap.height = state.imgHeight;\n      baseSnap.getContext('2d').drawImage(flatCanvas, 0, 0);\n      const res = await fetch('/api/image/inpaint', {\n        method: 'POST', credentials: 'same-origin',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify((() => {\n          const sel = getSelectedAIEndpoint('inpaint');\n          return { image: imageB64, mask: maskB64, prompt, width: state.imgWidth, height: state.imgHeight, strength, feather: 0, _endpoint: sel.endpoint, _model: sel.model };\n        })()),\n      });\n      if (!res.ok) {\n        let errDetail = res.statusText;\n        try { const errBody = await res.json(); errDetail = errBody.detail || errBody.error || errDetail; } catch {}\n        throw new Error(errDetail);\n      }\n      const data = await res.json();\n      if (data.error) throw new Error(data.error);\n      if (!data.image) throw new Error('No image returned from inpaint endpoint');\n      // Load result as a new layer and clip with the user-drawn mask\n      // so only the inpainted region is visible. Cache the\n      // unfeathered (AI image + hard mask) on the layer so the live\n      // Feather slider can re-derive the alpha on each input event\n      // without re-running the model.\n      const resultImg = new Image();\n      resultImg.onload = () => {\n        if (!state.editorOpen) return; // user closed mid-decode\n        try {\n          saveState('Inpaint result');\n          // OpenAI returns at one of its allowed sizes (1024²,\n          // 1024×1536, 1536×1024) which often differs from our\n          // canvas. Scale to canvas size with smoothing so the\n          // inpaint blends in regardless of source dims.","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/editor/ai-inpaint.js#L136-L172","documentation":"Thrown when POST /api/image/inpaint (AI inpainting in the image editor) responds non-2xx. The code prefers a JSON body field detail or error and falls back to res.statusText — which is frequently empty under HTTP/2, leaving the user with a blank error.","triggerScenarios":"Running Inpaint with a mask and prompt: the upstream AI provider key is missing/invalid (usually 500 with detail), the request payload exceeds the server's body limit (413), the selected custom endpoint in getSelectedAIEndpoint('inpaint') is unreachable, or credentials expired (401).","commonSituations":"No AI provider API key configured on the server; user-selected custom endpoint URL wrong or rate-limited; very large canvas producing a multi-MB base64 payload rejected by a proxy; statusText empty so 'Inpaint failed: ' shows nothing useful.","solutions":["Check the inpaint response body in the Network tab — the JSON detail usually names the upstream cause (missing key, provider 401, model name)","Verify an AI endpoint/model is selected and configured for the inpaint tool (getSelectedAIEndpoint)","Reduce image dimensions if the 413 body limit is the cause","Include res.status in the fallback message so it is never blank"],"exampleFix":"// before\nlet errDetail = res.statusText;\n\n// after\nlet errDetail = `HTTP ${res.status}`;","handlingStrategy":"try-catch","validationCode":"const sel = getSelectedAIEndpoint('inpaint');\nif (!sel.endpoint) { uiModule.showToast('Select an AI endpoint first'); return; }\nif (!imageB64 || !maskB64) { uiModule.showToast('Draw a mask before inpainting'); return; }","typeGuard":null,"tryCatchPattern":"if (!res.ok) { let errDetail = `HTTP ${res.status}`; try { const b = await res.json(); errDetail = b.detail || b.error || errDetail; } catch {} throw new Error(errDetail); }","preventionTips":["Use `HTTP ${res.status}` as fallback text, never bare statusText","Verify an endpoint/model is selected before building the payload","Cap exported image dimensions to stay under server body limits","Surfaced upstream detail strings usually name the missing key/model — read them"],"tags":["network","http","fetch","ai","inpaint","upstream-provider"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}