{"record":{"id":"bf00c68ee3ed8bc5","repo":"affaan-m/ECC","slug":"request-failed","errorCode":null,"errorMessage":"request failed","messagePattern":"request failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/control-pane/ui.js","lineNumber":664,"sourceCode":"    }\n\n    $('#query-form').addEventListener('submit', event => {\n      event.preventDefault();\n      state.query = $('#query').value.trim();\n      load().catch(error => showError('#app', error));\n    });\n    $('#refresh').addEventListener('click', () => {\n      load().catch(error => showError('#app', error));\n    });\n\n    async function postWorkItem(pathSuffix, payload) {\n      const response = await fetch('/api/work-items/' + pathSuffix, {\n        method: 'POST',\n        headers: { 'content-type': 'application/json' },\n        body: JSON.stringify(payload || {})\n      });\n      const result = await readJsonResponse(response);\n      if (!result.ok) throw new Error(result.error || 'request failed');\n      await load();\n    }\n\n    window.eccClaimItem = function (id) {\n      if (!state.allowActions) return;\n      const owner = window.prompt('Claim \"' + id + '\" as (owner name):');\n      if (!owner) return;\n      const as = (window.prompt(\"Owner kind: 'agent' or 'human'\", 'human') || '').trim().toLowerCase();\n      postWorkItem(encodeURIComponent(id) + '/claim', { owner: owner.trim(), as: as === 'agent' ? 'agent' : 'human' })\n        .catch(error => showError('#app', error));\n    };\n    window.eccMoveItem = function (id, lane) {\n      if (!state.allowActions) return;\n      postWorkItem(encodeURIComponent(id) + '/move', { lane })\n        .catch(error => showError('#app', error));\n    };\n\n    // Live board: refresh on a gentle interval; pause while a prompt/tab is hidden.","sourceCodeStart":646,"sourceCodeEnd":682,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/control-pane/ui.js#L646-L682","documentation":"Thrown by postWorkItem as a last-resort message when the control-pane POST responded with HTTP 200 and a JSON body whose result.ok is falsy, but result.error is empty/undefined. It means the server signaled failure (ok:false) without giving a reason, so the client cannot surface anything more specific than 'request failed'.","triggerScenarios":"A work-item mutation route (claim, move, etc.) returns { ok:false } with no error field; readJsonResponse passed because the body was valid JSON and response.ok was true, but the application-level success flag was false and the reason field was omitted.","commonSituations":"Server-side handler short-circuited (e.g. state.allowActions was false) and returned { ok:false } without populating error; a refactor changed the response shape from { error } to { reason } so the client's fallback kicks in; the route returns ok:false on a no-op but forgets to attach a message.","solutions":["On the server, always populate result.error (and result.reason) whenever ok is false so the client has something actionable.","On the client, surface result.reason || result.error before falling back, and log the full result object for diagnosis.","Reproduce with curl -X POST /api/work-items/<id>/<action> -H 'content-type: application/json' -d '{}' and inspect the JSON response.","Check whether allowActions gating or another precondition returned ok:false intentionally."],"exampleFix":"// before\nif (!result.ok) throw new Error(result.error || 'request failed');\n\n// after — surface any reason the server gave\nif (!result.ok) {\n  const detail = result.error || result.reason || `request failed (${JSON.stringify(result)})`;\n  throw new Error(detail);\n}","handlingStrategy":"validation","validationCode":"function assertOk(result) {\n  if (!result || result.ok === false) {\n    const detail = (result && (result.error || result.reason)) || 'request failed (no reason supplied)';\n    throw new Error(detail);\n  }\n}","typeGuard":"function isOkResult(result) {\n  return result && result.ok === true;\n}","tryCatchPattern":"try {\n  await postWorkItem(path, payload);\n} catch (e) {\n  if (e.message === 'request failed') {\n    // server returned ok:false with no error; inspect network/response in devtools\n  }\n  throw e;\n}","preventionTips":["Server-side: always set result.error (and result.reason) whenever ok is false.","Client-side: surface result.reason || result.error before the generic fallback.","Add a contract test asserting ok:false responses always include an error field."],"tags":["control-pane","api-contract","error-handling"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}