affaan-m/ECC · error
${result.error || 'request failed'}
Error message
${result.error || 'request failed'} What it means
Thrown by postWorkItem in the control pane UI after the server replies with a JSON body whose ok field is false (HTTP status was 200; readJsonResponse already handled non-2xx). The message is whatever the server put in result.error — typically one of the work-item mutation rejections: work item not found, invalid lane, claim requires an owner, or item already done.
Source
Thrown at scripts/lib/control-pane/ui.js:664
}
$('#query-form').addEventListener('submit', event => {
event.preventDefault();
state.query = $('#query').value.trim();
load().catch(error => showError('#app', error));
});
$('#refresh').addEventListener('click', () => {
load().catch(error => showError('#app', error));
});
async function postWorkItem(pathSuffix, payload) {
const response = await fetch('/api/work-items/' + pathSuffix, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(payload || {})
});
const result = await readJsonResponse(response);
if (!result.ok) throw new Error(result.error || 'request failed');
await load();
}
window.eccClaimItem = function (id) {
if (!state.allowActions) return;
const owner = window.prompt('Claim "' + id + '" as (owner name):');
if (!owner) return;
const as = (window.prompt("Owner kind: 'agent' or 'human'", 'human') || '').trim().toLowerCase();
postWorkItem(encodeURIComponent(id) + '/claim', { owner: owner.trim(), as: as === 'agent' ? 'agent' : 'human' })
.catch(error => showError('#app', error));
};
window.eccMoveItem = function (id, lane) {
if (!state.allowActions) return;
postWorkItem(encodeURIComponent(id) + '/move', { lane })
.catch(error => showError('#app', error));
};
// Live board: refresh on a gentle interval; pause while a prompt/tab is hidden.View on GitHub (pinned to d8409a4b08)
Solutions
- Read the surfaced message — it names the exact server-side rejection (e.g. 'Work item not found: X' or 'Invalid lane'), then fix that condition
- Click Refresh to reload current board state before retrying the action
- When claiming, make sure the owner prompt is answered with a non-empty name
- If the item was finished by another worker, pick a different item — the refusal is correct behavior
Defensive patterns
Strategy: try-catch
Validate before calling
if (!id) return;
const fresh = state.items.find(i => i.id === id);
if (!fresh) { await load(); return; } // stale card; reload instead of posting Try / catch
try {
await postWorkItem(`${encodeURIComponent(id)}/claim`, { owner });
} catch (error) {
showError('#app', error);
if (/not found|already done/.test(error.message)) {
await load(); // stale state — refresh the board, don't alarm the user
}
} Prevention
- Refresh the board before acting on items rendered long ago
- Always supply a non-empty owner when claiming
- Treat ok:false bodies as authoritative server state, not as bugs to retry blindly
When it happens
Trigger: Clicking Claim or Move on a card in a stale board where the item was deleted or already completed by someone else; submitting a claim with an empty owner; posting to /api/work-items/<id>/claim or /move with a payload the mutation layer rejects.
Common situations: Two agents, or a human plus an agent, using the control pane concurrently against the same DB; a UI tab left open while the underlying work-items DB changed; canceling or blanking the browser prompt dialogs.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- Work item not found: ${id}
- claim requires an owner.
- assigneeKind must be 'agent' or 'human'.
- Work item ${target.id} is already done; cannot claim.
- move requires a work item id.
AI-assisted analysis of affaan-m/ECC@d8409a4b08 (2026-08-26).
Data as JSON: /api/errors/4ef552d141bcb28a.
Report an issue: GitHub.