affaan-m/ECC · error · Error

(payload.error || status + ' ' + statusText)

Error message

(payload.error || status + ' ' + statusText)

What it means

After parsing, readJsonResponse checks response.ok. For non-2xx responses it throws the server-supplied payload.error, or falls back to '<status> <statusText>'. This surfaces HTTP-level failures from the control-pane API to the UI caller.

Solutions

  1. Read the thrown detail string — it is either the API's error message or the HTTP status; match it to the failing endpoint.
  2. Verify the request payload shape matches what the route handler validates.
  3. Check that the work item / route referenced still exists (stale UI state after another agent moved/deleted it).
  4. Add server-side logging in the route to capture why it returned a non-2xx status.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!response.ok) {
  const body = await response.text();
  console.error('API failed', response.status, body.slice(0, 500));
}

Type guard

function isApiErrorPayload(p) { return !!p && typeof p === 'object' && typeof p.error === 'string'; }

Try / catch

try { await postWorkItemAction(path, payload); } catch (e) {
  if (/^\d{3} /.test(e.message)) console.error('HTTP failure:', e.message);
  else throw e;
}

Prevention

When it happens

Trigger: Any POST/GET to /api/work-items/* that the server rejects: 400 bad body, 404 unknown route/id, 500 handler exception — thrown with the API's error string or the status code.

Common situations: Stale page posting a work item id that no longer exists (404), invalid lane/action payload (400), or server bug producing 500 with an error field.

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


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

Appendix: source

Thrown at scripts/lib/control-pane/ui.js:449

    function clearError(targetSelector) {
      const target = $(targetSelector);
      if (!target) return;
      target.hidden = true;
      target.textContent = '';
    }

    async function readJsonResponse(response) {
      let payload;
      try {
        payload = await response.json();
      } catch (error) {
        throw new Error('Expected JSON response from control pane: ' + error.message);
      }

      if (!response.ok) {
        const detail = payload && payload.error ? payload.error : response.status + ' ' + response.statusText;
        throw new Error(detail);
      }

      return payload;
    }

    function statePill(stateName) {
      const state = String(stateName || 'unknown');
      const klass = ['running', 'done'].includes(state)
        ? 'good'
        : ['failed', 'blocked'].includes(state)
          ? 'bad'
          : ['pending', 'ready'].includes(state)
            ? 'warn'
            : 'blue';
      return '<span class="pill ' + klass + '">' + escapeHtml(state) + '</span>';
    }

    function renderMetrics(summary) {

View on GitHub (pinned to 8321021c54)