affaan-m/ECC · error
HTTP + res.status
Error message
HTTP + res.status
What it means
This error comes from the Plan Canvas browser client's feedback queue flush: after POSTing queued feedback items to /api/session/<key>/feedback, the client checks res.ok and throws 'HTTP <status>' when the server returned an error status (400/404/500, etc.). The thrown value's message is literally the string 'HTTP ' + status, so the browser console shows e.g. 'Uncaught (in promise) Error: HTTP 500'.
Solutions
- Reload the canvas page to get a fresh session key, then re-submit the feedback.
- Check the canvas server logs for the corresponding request to see the 4xx/5xx reason.
- If the server was restarted, restart or reconnect your agent session to the canvas so keys realign.
- Clear the local feedback queue (page reload with a healthy server) if persisted items are stale/corrupt and keep failing.
Example fix
// before (client throws and drops nothing)
if (!res.ok) throw new Error('HTTP ' + res.status);
// after (caller-side handling: catch, keep queue, surface status)
try { await flushFeedback(queue); }
catch (e) { console.warn('Feedback submit failed:', e.message, '- will retry'); }
// plus: reload the page if the error is HTTP 404 (stale session key) Defensive patterns
Strategy: try-catch
Validate before calling
// before flushing queued feedback
if (!queue.length) return;
if (typeof navigator !== 'undefined' && !navigator.onLine) {
console.warn('Offline: feedback will stay queued');
return;
}
await fetch('/api/session/' + key + '/feedback', { method: 'HEAD' }).then(r => {
if (r.status === 404) console.warn('Stale session key — reload the page before submitting.');
}); Type guard
function isFreshSession(session) {
return Boolean(session && session.key && Date.now() - session.startedAt < 6 * 60 * 60 * 1000);
} Try / catch
try {
await flushFeedback(queue);
} catch (e) {
if (/^HTTP 404$/.test(e.message)) {
console.warn('Session expired (canvas server restarted). Reload the page and re-submit.');
} else if (/^HTTP \d+$/.test(e.message)) {
console.warn(`Feedback rejected (${e.message}); items kept in queue for retry.`);
} else throw e;
} Prevention
- Reload the canvas page after the server restarts so session keys stay valid.
- Handle fetch failures by keeping the local queue instead of clearing it on error.
- Check server connectivity before long feedback batches; don't leave tabs open across server restarts.
- Match client and server versions — stale tabs posting new server-incompatible payloads cause 4xx errors.
When it happens
Trigger: The feedback POST fails server-side: session key no longer exists (canvas restarted/stale session, 404), malformed queue items (400), or a server error while persisting feedback (500). Also occurs offline or when the canvas server was restarted, losing the session.
Common situations: Leaving a canvas tab open overnight; the server idles out or is restarted, invalidating the session key; submitting a batch of feedback items while the network dropped; a bug in queued item shape after a client/server version mismatch (old tab, new server).
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
- Cannot delete active session
- Cannot merge active session
- Cannot rebase active session
- Canonical session snapshot requires
- Claude session not found
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/c5732c89de07e381.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/plan-canvas/ui.js:385
if (ended || sending) return;
const items = queue.slice();
if (extraItems) items.push(...extraItems);
const text = input.value.trim();
if (text) items.push({ kind: 'chat', text });
if (!items.length) {
statusEl.textContent = 'Nothing to send yet - annotate the plan or type a message.';
return;
}
sending = true;
sendBtn.disabled = true;
statusEl.textContent = 'Sending\\u2026';
try {
const res = await fetch('/api/session/' + key + '/feedback', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ items })
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const body = await res.json().catch(() => ({}));
queue = [];
persistQueue();
renderQueue();
input.value = '';
// Say what actually happened: a parked agent takes the batch on the
// spot, otherwise it sits in the queue until the agent checks in.
statusEl.textContent = body.presence === 'thinking' || body.presence === 'typing'
? 'Delivered. Your agent has it.'
: 'Queued. Your agent picks this up the moment it checks in.';
if (body.presence) applyPresence(body.presence);
} catch (err) {
statusEl.textContent = 'Send failed (' + err.message + ') - is the canvas server still running?';
} finally {
sending = false;
sendBtn.disabled = ended;
}
}View on GitHub (pinned to 8321021c54)