affaan-m/ECC · error · Error
Invalid control-plane view
Error message
Invalid control-plane view
What it means
The control-plane view UI validates every payload applied to the view: it must carry schemaVersion 'ecc.control-plane.view.v1', array fields tasks/lanes/pairs/events, and a projection with finite thresholds ta/ra. A payload failing any check throws 'Invalid control-plane view', indicating the server sent data that doesn't match the expected v1 schema.
Solutions
- Hard-reload the control-plane page so the client JS matches the server version
- Inspect the /api/control-plane response and verify schemaVersion === 'ecc.control-plane.view.v1' and all required array/threshold fields
- Update the server and client to the same version if you pinned one of them
- Fix the snapshot builder on the server so it always emits the full v1 view shape
Example fix
// before
fetch('/api/control-plane').then(r => r.json()).then(apply);
// after
fetch('/api/control-plane').then(r => r.json()).then(data => {
if (data && data.schemaVersion === 'ecc.control-plane.view.v1') apply(data);
else console.warn('skipping incompatible view payload', data && data.schemaVersion);
}); Defensive patterns
Strategy: type-guard
Validate before calling
function isValidView(d) {
return !!d && d.schemaVersion === 'ecc.control-plane.view.v1'
&& ['tasks','lanes','pairs','events'].every(k => Array.isArray(d[k]))
&& d.projection && Array.isArray(d.projection.agents)
&& d.thresholds && Number.isFinite(d.thresholds.ta) && Number.isFinite(d.thresholds.ra);
}
// call apply only if isValidView(data) Type guard
const isValidView = (d) => !!d && d.schemaVersion === 'ecc.control-plane.view.v1' && ['tasks','lanes','pairs','events'].every(k => Array.isArray(d[k])) && d.projection && Array.isArray(d.projection.agents) && d.thresholds && Number.isFinite(d.thresholds.ta) && Number.isFinite(d.thresholds.ra);
Try / catch
try { apply(data); } catch (e) {
if (e.message === 'Invalid control-plane view') setStatus('incompatible view payload; reload page');
else throw e;
} Prevention
- Keep client JS and server schema versions in lockstep (hard reload after upgrades)
- Validate payloads before apply when fetching from proxies/CDNs
- Ensure the snapshot builder always emits all required arrays
- Avoid caching /api/control-plane responses across schema versions
When it happens
Trigger: apply(data) is invoked (directly or via the /api/control-plane poll) with data that is null, has a wrong schemaVersion, has a non-array tasks/lanes/pairs/events field, or lacks a projection.agents array or finite thresholds.ta/thresholds.ra.
Common situations: Server and client versions disagree (schema changed but cache served an old page); a proxy returned an HTML error page parsed loosely; the snapshot builder emitted a partial/empty payload; stale cached JSON in localStorage or a CDN.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- -32602
- artifact path must be a non-empty relative path
- assets must be a nonempty list
- built an invalid spec
- Canonical session snapshot must be an object
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/99123425ee7c1e12.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/control-pane/control-plane-view-ui.js:214
var row = document.createElement('div'); row.className = 'task';
var idEl = document.createElement('span'); idEl.className = 'id'; idEl.textContent = t.id.slice(0, 20);
var st = document.createElement('span'); st.textContent = t.harness + ' / ' + t.state + ' / ' + (t.workingSet.fileCount || 0) + ' files';
var risk = document.createElement('span'); risk.className = 'risk';
risk.style.color = riskColor(t.projection.maxRisk || 0);
risk.textContent = t.projection.point ? Math.round((t.projection.maxRisk || 0) * 100) + '%' : 'no pair';
row.appendChild(idEl); row.appendChild(st); row.appendChild(risk);
el.appendChild(row);
});
box.appendChild(el);
});
}
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';
});
}View on GitHub (pinned to 8321021c54)