{"record":{"id":"c5732c89de07e381","repo":"affaan-m/ECC","slug":"http-res-status-ui","errorCode":null,"errorMessage":"HTTP + res.status","messagePattern":"HTTP \\+ res\\.status","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/plan-canvas/ui.js","lineNumber":385,"sourceCode":"    if (ended || sending) return;\n    const items = queue.slice();\n    if (extraItems) items.push(...extraItems);\n    const text = input.value.trim();\n    if (text) items.push({ kind: 'chat', text });\n    if (!items.length) {\n      statusEl.textContent = 'Nothing to send yet - annotate the plan or type a message.';\n      return;\n    }\n    sending = true;\n    sendBtn.disabled = true;\n    statusEl.textContent = 'Sending\\\\u2026';\n    try {\n      const res = await fetch('/api/session/' + key + '/feedback', {\n        method: 'POST',\n        headers: { 'content-type': 'application/json' },\n        body: JSON.stringify({ items })\n      });\n      if (!res.ok) throw new Error('HTTP ' + res.status);\n      const body = await res.json().catch(() => ({}));\n      queue = [];\n      persistQueue();\n      renderQueue();\n      input.value = '';\n      // Say what actually happened: a parked agent takes the batch on the\n      // spot, otherwise it sits in the queue until the agent checks in.\n      statusEl.textContent = body.presence === 'thinking' || body.presence === 'typing'\n        ? 'Delivered. Your agent has it.'\n        : 'Queued. Your agent picks this up the moment it checks in.';\n      if (body.presence) applyPresence(body.presence);\n    } catch (err) {\n      statusEl.textContent = 'Send failed (' + err.message + ') - is the canvas server still running?';\n    } finally {\n      sending = false;\n      sendBtn.disabled = ended;\n    }\n  }","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/plan-canvas/ui.js#L367-L403","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before (client throws and drops nothing)\nif (!res.ok) throw new Error('HTTP ' + res.status);\n// after (caller-side handling: catch, keep queue, surface status)\ntry { await flushFeedback(queue); }\ncatch (e) { console.warn('Feedback submit failed:', e.message, '- will retry'); }\n// plus: reload the page if the error is HTTP 404 (stale session key)","handlingStrategy":"try-catch","validationCode":"// before flushing queued feedback\nif (!queue.length) return;\nif (typeof navigator !== 'undefined' && !navigator.onLine) {\n  console.warn('Offline: feedback will stay queued');\n  return;\n}\nawait fetch('/api/session/' + key + '/feedback', { method: 'HEAD' }).then(r => {\n  if (r.status === 404) console.warn('Stale session key — reload the page before submitting.');\n});","typeGuard":"function isFreshSession(session) {\n  return Boolean(session && session.key && Date.now() - session.startedAt < 6 * 60 * 60 * 1000);\n}","tryCatchPattern":"try {\n  await flushFeedback(queue);\n} catch (e) {\n  if (/^HTTP 404$/.test(e.message)) {\n    console.warn('Session expired (canvas server restarted). Reload the page and re-submit.');\n  } else if (/^HTTP \\d+$/.test(e.message)) {\n    console.warn(`Feedback rejected (${e.message}); items kept in queue for retry.`);\n  } else throw e;\n}","preventionTips":["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."],"tags":["http","client-side","fetch","session","api-error"],"backgroundTag":"http-error-response","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}