affaan-m/ECC · error · Error

request failed

Error message

request failed

What it means

Work-item action posts return { ok: boolean, error?: string }. When the handler completes successfully at HTTP level but reports ok:false, the client throws result.error or the generic 'request failed' fallback. This is a business-logic failure, not an HTTP failure.

Solutions

  1. Look at the server handler for the endpoint and find which path returns ok:false; the generic message means the error field was missing.
  2. Ensure handlers always include an error string when ok is false: { ok: false, error: reason }.
  3. Re-check the work item state — the mutation may have been rejected because the item is done, claimed, or missing.
  4. Retry the action after refreshing the control pane state.

Example fix

// before (server handler)
return res.json({ ok: false });
// after
return res.json({ ok: false, error: 'no-unassigned-open-items' });
Defensive patterns

Strategy: type-guard

Validate before calling

const result = await readJsonResponse(response);
if (typeof result.ok !== 'boolean') throw new Error('Malformed API result');
if (!result.ok && !result.error) throw new Error('API failed without error detail');

Type guard

function isActionOk(r) { return !!r && r.ok === true; }

Try / catch

try { await action(); } catch (e) {
  if (e.message === 'request failed') console.error('Handler returned ok:false without an error message — inspect the server handler.');
  else throw e;
}

Prevention

When it happens

Trigger: POST /api/work-items/<action> whose handler returns { ok: false } without an error message — e.g. mutation helper returned { claimed:false } path collapsed into a falsey ok, or handler omitted the error field.

Common situations: Claiming/moving a work item when the store operation partially fails, or a handler that sets ok:false but forgets to set error, so the UI shows only 'request failed'.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/bf00c68ee3ed8bc5. Report an issue: GitHub.

Appendix: 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 8321021c54)