odysseus-dev/odysseus · error · Error
No image returned
Error message
No image returned
What it means
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.
Source
Thrown at static/js/editor/ai-tools-misc.js:149
saveState();
const newW = img.width, newH = img.height;
const layer = createLayer('AI Upscaled', newW, newH);
layer.ctx.drawImage(img, 0, 0);
state.layers.push(layer);
state.activeLayerId = layer.id;
state.imgWidth = newW; state.imgHeight = newH;
state.mainCanvas.width = newW; state.mainCanvas.height = newH;
if (state.maskCanvas) { state.maskCanvas.width = newW; state.maskCanvas.height = newH; }
const sizeLabel = document.getElementById('ge-canvas-size');
if (sizeLabel) sizeLabel.textContent = `${newW}×${newH}`;
fitZoom();
composite();
renderLayerPanel();
uiModule.showToast(`AI upscaled to ${newW}×${newH}`);
};
img.src = 'data:image/png;base64,' + data.image;
} else {
throw new Error(data.error || 'No image returned');
}
} catch (e) {
uiModule.showToast('AI upscale failed: ' + e.message);
}
try { upWp?.destroy(); } catch (_) {}
btn.disabled = false;
btn.innerHTML = origHTML;
});
// ── Style transfer ──
document.getElementById('ge-style-strength')?.addEventListener('input', (e) => {
document.getElementById('ge-style-strength-label').textContent = (parseInt(e.target.value) / 100).toFixed(2);
});
document.getElementById('ge-style-run')?.addEventListener('click', async () => {
const btn = document.getElementById('ge-style-run');
const prompt = document.getElementById('ge-style-prompt').value.trim();
if (!prompt) { uiModule.showToast('Enter a style prompt'); return; }
const strength = parseInt(document.getElementById('ge-style-strength').value) / 100;View on GitHub (pinned to f9235ebbf1)
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
Example fix
// before
throw new Error(data.error || 'No image returned');
// after
throw new Error(data.error || `No image returned (keys: ${Object.keys(data).join(',') || 'none'})`); Defensive patterns
Strategy: type-guard
Type guard
function isUpscaleResult(d) { return d != null && typeof d === 'object' && ('image' in d || 'error' in d); } Try / catch
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'); Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- statusText
- data.error
- Server returned ${res.status}
- No uploaded files returned
- Document create failed: missing id
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/671bd7b7a16ca2c6.
Report an issue: GitHub.