{"record":{"id":"9ea21d07fc79e7e1","repo":"odysseus-dev/odysseus","slug":"statustext-9ea21d","errorCode":null,"errorMessage":"statusText","messagePattern":"statusText","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/editor/ai-tool-runner.js","lineNumber":89,"sourceCode":"      const m = /\\/api\\/image\\/([\\w-]+)/.exec(endpoint || '');\n      const type = m ? m[1].replace('upscale-ai', 'upscale').replace('remove-bg', 'rembg') : null;\n      const sel = getSelectedAIEndpoint(type);\n      if (sel.endpoint) extraPayload._endpoint = sel.endpoint;\n      if (sel.model && !extraPayload._model) extraPayload._model = sel.model;\n    }\n    try {\n      const flatCanvas = flatten();\n      const imageB64 = flatCanvas.toDataURL('image/png').split(',')[1];\n      const body = { image: imageB64, ...extraPayload };\n      const res = await fetch(endpoint, {\n        method: 'POST', credentials: 'same-origin',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify(body),\n      });\n      if (!res.ok) {\n        let err = res.statusText;\n        try { const e = await res.json(); err = e.detail || e.error || err; } catch {}\n        throw new Error(err);\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');\n      const img = new Image();\n      img.onload = () => {\n        if (!state.editorOpen) return; // user closed mid-decode (v2 review HIGH-4)\n        saveState();\n        const layer = createLayer(layerName, state.imgWidth, state.imgHeight);\n        layer.ctx.drawImage(img, 0, 0);\n        state.layers.push(layer);\n        state.activeLayerId = layer.id;\n        composite();\n        renderLayerPanel();\n        if (uiModule) uiModule.showToast(layerName + ' complete', 4500);\n      };\n      img.onerror = () => { if (uiModule) uiModule.showToast('Failed to load result', 6000); };\n      img.src = 'data:image/png;base64,' + data.image;","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/editor/ai-tool-runner.js#L71-L107","documentation":"Generic AI-editor tool runner: thrown when the POST to the configured image endpoint returns non-2xx. It prefers the JSON body's detail/error fields and falls back to res.statusText, which can be empty over HTTP/2.","triggerScenarios":"Any editor AI tool routed through runAITool (expand, variations, etc.) failing at the HTTP level: 401 expired session, 422 payload validation, 413 oversized base64 image, 502 when the AI provider proxy is down, or an unknown endpoint path when endpoint is misconfigured.","commonSituations":"Server deployed without the AI routes mounted; endpoint variable built from stale config after a backend update; large canvas images exceeding proxy body limits; provider outage surfacing as 502 with an HTML body that fails JSON parsing.","solutions":["Inspect the failing request's status and body in DevTools to separate auth/validation/upstream causes","Confirm the endpoint path matches a route the current backend actually serves","Shrink the exported PNG (flatten() output) if body size is the issue","Use `HTTP ${res.status}` as the fallback message instead of bare statusText"],"exampleFix":"// before\nlet err = res.statusText;\n\n// after\nlet err = `HTTP ${res.status}`;","handlingStrategy":"try-catch","validationCode":"if (!endpoint || typeof endpoint !== 'string' || !endpoint.startsWith('/')) { uiModule.showToast('Invalid AI tool endpoint'); return; }","typeGuard":null,"tryCatchPattern":"if (!res.ok) { let err = `HTTP ${res.status}`; try { const e = await res.json(); err = e.detail || e.error || err; } catch {} throw new Error(err); }","preventionTips":["Centralize the ok/detail/error/status fallback in one helper reused by all AI tools","Include status codes in every fallback message","Version-pin frontend endpoints with the backend routes table","Test tool endpoints after backend upgrades before users hit them"],"tags":["network","http","fetch","ai","editor","generic-tool"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}