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

  1. Log/inspect the actual response body of the upscale call to see which key holds the image
  2. Align frontend and backend versions so the {image: <base64>} contract holds
  3. If you own the backend, always return either image or a descriptive error field
  4. 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

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


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/671bd7b7a16ca2c6. Report an issue: GitHub.