odysseus-dev/odysseus · error · Error
data.error
Error message
data.error
What it means
Thrown by the generic AI tool runner when the endpoint responds 200 but the JSON contains an error field — the backend swallowed an upstream/provider failure into a successful response. The message shown to the user is exactly that embedded error string.
Source
Thrown at static/js/editor/ai-tool-runner.js:92
if (sel.endpoint) extraPayload._endpoint = sel.endpoint;
if (sel.model && !extraPayload._model) extraPayload._model = sel.model;
}
try {
const flatCanvas = flatten();
const imageB64 = flatCanvas.toDataURL('image/png').split(',')[1];
const body = { image: imageB64, ...extraPayload };
const res = await fetch(endpoint, {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!res.ok) {
let err = res.statusText;
try { const e = await res.json(); err = e.detail || e.error || err; } catch {}
throw new Error(err);
}
const data = await res.json();
if (data.error) throw new Error(data.error);
if (!data.image) throw new Error('No image returned');
const img = new Image();
img.onload = () => {
if (!state.editorOpen) return; // user closed mid-decode (v2 review HIGH-4)
saveState();
const layer = createLayer(layerName, state.imgWidth, state.imgHeight);
layer.ctx.drawImage(img, 0, 0);
state.layers.push(layer);
state.activeLayerId = layer.id;
composite();
renderLayerPanel();
if (uiModule) uiModule.showToast(layerName + ' complete', 4500);
};
img.onerror = () => { if (uiModule) uiModule.showToast('Failed to load result', 6000); };
img.src = 'data:image/png;base64,' + data.image;
} catch (e) {
// Detect known failure modes and surface an action-toast.
const msg = (e?.message || '').toLowerCase();View on GitHub (pinned to f9235ebbf1)
Solutions
- Use the literal data.error text to identify the provider-side cause
- Correct the model/endpoint selection or provider credentials it complains about
- Wait out rate limits and retry the tool once
- On the backend, map provider failures to real HTTP error statuses for observability
Defensive patterns
Strategy: validation
Validate before calling
const data = await res.json(); if (data.error) throw new Error(data.error);
Type guard
function hasImageData(d) { return d != null && typeof d.image === 'string' && d.image.length > 0; } Try / catch
try { const data = await res.json(); if (data.error) throw new Error(data.error); if (!hasImageData(data)) throw new Error('No image returned'); ... } catch (e) { uiModule.showToast(`${layerName} failed: ` + e.message); } Prevention
- Check data.error on every 200 response from AI endpoints — this backend embeds failures in success bodies
- Guard with hasImageData() before creating layers
- Keep provider key/model config validated at tool-open time, not just at submit
When it happens
Trigger: Provider rejects the request after the server accepted it: bad API key for the chosen model, quota/rate limit, safety filter, or a malformed provider response the server converted into {error: ...}.
Common situations: Rotated provider key not yet updated server-side; user picks a model the plan does not include; provider throttling during heavy editing sessions; server catch-all handlers returning 200 with error payloads.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/54a3e123d3230112.
Report an issue: GitHub.