{"record":{"id":"671bd7b7a16ca2c6","repo":"odysseus-dev/odysseus","slug":"no-image-returned","errorCode":null,"errorMessage":"No image returned","messagePattern":"No image returned","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/editor/ai-tools-misc.js","lineNumber":149,"sourceCode":"          saveState();\n          const newW = img.width, newH = img.height;\n          const layer = createLayer('AI Upscaled', newW, newH);\n          layer.ctx.drawImage(img, 0, 0);\n          state.layers.push(layer);\n          state.activeLayerId = layer.id;\n          state.imgWidth = newW; state.imgHeight = newH;\n          state.mainCanvas.width = newW; state.mainCanvas.height = newH;\n          if (state.maskCanvas) { state.maskCanvas.width = newW; state.maskCanvas.height = newH; }\n          const sizeLabel = document.getElementById('ge-canvas-size');\n          if (sizeLabel) sizeLabel.textContent = `${newW}×${newH}`;\n          fitZoom();\n          composite();\n          renderLayerPanel();\n          uiModule.showToast(`AI upscaled to ${newW}×${newH}`);\n        };\n        img.src = 'data:image/png;base64,' + data.image;\n      } else {\n        throw new Error(data.error || 'No image returned');\n      }\n    } catch (e) {\n      uiModule.showToast('AI upscale failed: ' + e.message);\n    }\n    try { upWp?.destroy(); } catch (_) {}\n    btn.disabled = false;\n    btn.innerHTML = origHTML;\n  });\n\n  // ── Style transfer ──\n  document.getElementById('ge-style-strength')?.addEventListener('input', (e) => {\n    document.getElementById('ge-style-strength-label').textContent = (parseInt(e.target.value) / 100).toFixed(2);\n  });\n  document.getElementById('ge-style-run')?.addEventListener('click', async () => {\n    const btn = document.getElementById('ge-style-run');\n    const prompt = document.getElementById('ge-style-prompt').value.trim();\n    if (!prompt) { uiModule.showToast('Enter a style prompt'); return; }\n    const strength = parseInt(document.getElementById('ge-style-strength').value) / 100;","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/editor/ai-tools-misc.js#L131-L167","documentation":"Thrown after a successful upscale HTTP response when the JSON has neither image nor error fields (message falls back to 'No image returned'). It means the endpoint's success shape did not match the expected {image: base64} contract.","triggerScenarios":"POST /api/image/upscale-local returns 200 with an unexpected body — e.g. {images: [...]}, a URL instead of base64, or an empty object from a partially updated backend handler.","commonSituations":"Backend response schema changed between frontend/backend versions; a new handler returning a different key; a proxy stripping/rewriting the response; server returning {} after an internal silent catch.","solutions":["Log/inspect the actual response body of the upscale call to see which key holds the image","Align frontend and backend versions so the {image: <base64>} contract holds","If you own the backend, always return either image or a descriptive error field","Include the response keys in the message to speed up diagnosis"],"exampleFix":"// before\nthrow new Error(data.error || 'No image returned');\n\n// after\nthrow new Error(data.error || `No image returned (keys: ${Object.keys(data).join(',') || 'none'})`);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isUpscaleResult(d) { return d != null && typeof d === 'object' && ('image' in d || 'error' in d); }","tryCatchPattern":"const data = await res.json(); if (!isUpscaleResult(data) || (!data.image && !data.error)) throw new Error('Unexpected upscale response: ' + Object.keys(data||{}).join(',')); if (!data.image) throw new Error(data.error || 'No image returned');","preventionTips":["Validate the response contract (image|error) before use","Deploy frontend and backend together when response schemas change","Include received keys in mismatch messages for quick diagnosis"],"tags":["ai","upscale","contract-mismatch","response-shape","editor"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}