pbakaus/impeccable · error · Error

errBody.error || ('HTTP ' + res.status)

Error message

errBody.error || ('HTTP ' + res.status)

What it means

The live-mode in-page script POSTs pending copy edits to the local dev server (/...stash endpoint) and, on a non-OK HTTP response, throws Error(errBody.error || 'HTTP <status>'). The message is either the server's JSON error field or a bare status code if the body is not JSON.

Source

Thrown at skill/scripts/live-browser.js:3801

    if (container) for (const op of ops) op.container = container;
    try {
      // Token in the query string as well as the body: the URL token is what
      // authorizes the CORS preflight when the page runs on a non-loopback
      // dev host (ddev, Valet), since the preflight carries no request body.
      const res = await fetch('http://localhost:' + PORT + '/manual-edit-stash?token=' + encodeURIComponent(TOKEN), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          token: TOKEN,
          id: id8(),
          pageUrl: location.pathname,
          element: extractContext(contextElement),
          ops,
        }),
      });
      if (!res.ok) {
        const errBody = await res.json().catch(() => ({}));
        throw new Error(errBody.error || ('HTTP ' + res.status));
      }
      const stashResult = await res.json();
      updatePendingCounter(stashResult.pendingCount || 0);
      maybeShowFirstSaveToast();
      disableInlineEdit();
      setLiveState('CONFIGURING');
      showBar('configure');
      showAnnotOverlay(selectedElement);
      renderEditBadge('idle');
    } catch (err) {
      console.error('[impeccable] manual edit stash failed:', err);
      const detail = String(err?.message || '');
      if (detail.includes('newText cannot contain') || detail.includes('newText cannot be empty')) {
        showToast('Save rejected: ' + detail.replace(/^manual_edits:\s*/, ''), 5500);
      } else {
        showToast('Save failed - retry or cancel', 4000);
      }
    }

View on GitHub (pinned to 2bc2879276)

Solutions

  1. Check the thrown message and the dev-server console for the underlying error field
  2. Verify the dev server is running on the expected PORT and the page TOKEN matches the current session
  3. Refresh the page to re-handshake token/state, then re-apply the edit
  4. If the status is 5xx, inspect server logs for the failing request handler

Example fix

// caller hardening
try {
  await stashEdits(ops);
} catch (e) {
  showToast('Save failed: ' + e.message + ' - is the dev server running?', 4000);
}
Defensive patterns

Strategy: try-catch

Validate before calling

async function devServerReachable(port) {
  try { const r = await fetch('http://localhost:' + port + '/health'); return r.ok; }
  catch { return false; }
}
// check before attempting the stash POST

Try / catch

try {
  const res = await fetch(stashUrl, { method: 'POST', body });
  if (!res.ok) {
    const errBody = await res.json().catch(() => ({}));
    throw new Error(errBody.error || ('HTTP ' + res.status));
  }
} catch (e) {
  showToast('Save failed: ' + e.message, 4000); // and keep edits pending locally
}

Prevention

When it happens

Trigger: Saving edits while the local impeccable dev-server returns 4xx/5xx, is down, or responds with a non-JSON error body.

Common situations: Dev server restarted with a different token/port; server rejected the edit payload (validation); proxy or firewall intercepting localhost requests.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08). Data as JSON: /api/errors/e25910a5f0fdb00f. Report an issue: GitHub.