affaan-m/ECC · error · Error

Control-plane request failed

Error message

Control-plane request failed

What it means

The control-plane view polls /api/control-plane and throws 'Control-plane request failed' whenever the fetch resolves but the HTTP response is not ok (non-2xx). The poll's catch handler then sets the status display to 'offline', so users see the dashboard go offline while the server is erroring.

Solutions

  1. Check the control-plane server logs for the error behind the non-2xx response
  2. Verify the page is served from the same server that exposes /api/control-plane (correct --host/--port)
  3. Curl /api/control-plane directly and inspect the status code and body
  4. Restart the control-plane server or fix the failing snapshot endpoint

Example fix

// before
fetch('/api/control-plane').then(r => { if (!r.ok) throw new Error('Control-plane request failed'); ... });
// after
fetch('/api/control-plane').then(r => {
  if (!r.ok) throw new Error(`Control-plane request failed: HTTP ${r.status}`);
  return r.json();
}).then(apply).catch(err => { setStatus('offline'); console.warn(err.message); });
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch('/api/control-plane');
if (!res.ok) console.warn('control-plane HTTP', res.status); // decide before calling apply

Type guard

const isHealthy = (r) => r.ok;

Try / catch

fetch('/api/control-plane')
  .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
  .then(apply)
  .catch(err => { setStatus('offline'); console.warn('control-plane unreachable:', err.message); });

Prevention

When it happens

Trigger: fetch('/api/control-plane') returns r.ok === false — e.g. 500 from a server-side snapshot failure, 404 after the route was moved, or 502/503 from a proxy while the control-plane server restarts.

Common situations: Control-plane server crashed or is restarting; wrong port/host so requests hit another service; API route renamed in an upgrade; proxy (nginx/traefik) returning 502 when the upstream is down.

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/062f6f826c4c8189. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/control-pane/control-plane-view-ui.js:227

  function apply(data) {
    if (!data || data.schemaVersion !== 'ecc.control-plane.view.v1' ||
        !['tasks', 'lanes', 'pairs', 'events'].every(function (key) { return Array.isArray(data[key]); }) ||
        !data.projection || !Array.isArray(data.projection.agents) || !data.thresholds ||
        !Number.isFinite(data.thresholds.ta) || !Number.isFinite(data.thresholds.ra)) {
      throw new Error('Invalid control-plane view');
    }
    view = Object.assign({}, data);
    renderEvents(); renderLanes(); draw();
    var c = view.counts || {};
    document.getElementById('status').textContent =
      (c.tasks || 0) + ' tasks in ' + (c.lanes || 0) + ' lanes | ' + (c.agents || 0) + ' with edits | ' +
      (c.advisories || 0) + ' advisories (' + (c.resolutions || 0) + ' steering)' +
      (view.inventory && view.inventory.status !== 'ok' ? ' | inventory ' + view.inventory.status : '');
  }

  function poll() {
    fetch('/api/control-plane').then(function (r) {
      if (!r.ok) throw new Error('Control-plane request failed');
      return r.json();
    }).then(apply).catch(function () {
      document.getElementById('status').textContent = 'offline';
    });
  }

  resize();
  poll();
  setInterval(poll, 5000);
})();
</script>
</body>
</html>`;
}

module.exports = { renderControlPlaneViewHtml };

View on GitHub (pinned to 8321021c54)