{"record":{"id":"7d1e295a8ad707aa","repo":"affaan-m/ECC","slug":"unknown-control-pane-action-actionid","errorCode":null,"errorMessage":"Unknown control-pane action: ${actionId}","messagePattern":"Unknown control-pane action: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/control-pane/actions.js","lineNumber":99,"sourceCode":"  const text = String(value);\n  if (text.length === 0) return \"''\";\n  if (/^[A-Za-z0-9_./:=@%+-]+$/.test(text)) return text;\n  return `'${text.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction commandLineFor(action) {\n  return [\n    `cd ${shellQuote(action.cwd)}`,\n    '&&',\n    shellQuote(action.command),\n    ...action.args.map(shellQuote),\n  ].join(' ');\n}\n\nfunction buildControlPaneAction(actionId, options = {}) {\n  const definition = ACTION_DEFINITIONS.get(actionId);\n  if (!definition) {\n    throw new Error(`Unknown control-pane action: ${actionId}`);\n  }\n\n  const repoRoot = path.resolve(options.repoRoot || process.cwd());\n  const cwd = path.join(repoRoot, 'ecc2');\n  const limit = normalizeLimit(options.limit);\n  const query = String(options.query || '').trim();\n  const args = definition.args({ limit, query });\n  const action = {\n    id: actionId,\n    label: definition.label,\n    description: definition.description,\n    command: 'cargo',\n    args,\n    cwd,\n    executable: definition.executable,\n  };\n\n  return {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/control-pane/actions.js#L81-L117","documentation":"Thrown by buildControlPaneAction at scripts/lib/control-pane/actions.js:97-100 when actionId is not a key in the ACTION_DEFINITIONS Map. The control pane exposes a fixed set of ECC2 graph actions — sync-knowledge, recall-knowledge, graph-sync, open-dashboard — each mapped to a cargo invocation against the ecc2 workspace. Unknown ids are rejected before any cargo command is constructed. The function is called both directly from the HTTP server's POST /api/actions/:id route (server.js:271) and from buildControlPaneActions which iterates all known keys.","triggerScenarios":"POST /api/actions/sync to the control-pane server (truncated/misspelled id); POST /api/actions/recall-knowledge%2Fextra (path-traversal-ish id); a UI button wired to a stale action id after an upgrade that renamed or removed an action; calling buildControlPaneAction('refresh', {}) from a script.","commonSituations":"Custom UI dashboards referencing actions that were renamed across versions; URL-encoded typos; clients POSTing the action label ('Sync Knowledge') instead of the id ('sync-knowledge'); attackers probing arbitrary action ids against the loopback server.","solutions":["Use one of the known ids: sync-knowledge, recall-knowledge, graph-sync, open-dashboard.","Call GET /api/snapshot or buildControlPaneActions() to enumerate the live action ids for your version.","If you need a new action, register it in the ACTION_DEFINITIONS Map at actions.js:5-72 (label, description, args factory, executable flag).","Note: even valid ids that are non-executable (open-dashboard, executable:false) return a 400 from the server rather than running — see server.js:277-285."],"exampleFix":"// before\nfetch('http://127.0.0.1:8765/api/actions/sync', { method: 'POST' });\n// after\nfetch('http://127.0.0.1:8765/api/actions/sync-knowledge', { method: 'POST', body: '{}' });","handlingStrategy":"validation","validationCode":"const KNOWN = new Set(['sync-knowledge', 'recall-knowledge', 'graph-sync', 'open-dashboard']);\nfunction assertKnownAction(actionId) {\n  if (!KNOWN.has(actionId)) throw new Error(`Unknown control-pane action: ${actionId}`);\n  return actionId;\n}","typeGuard":"function isControlPaneActionId(value) {\n  return typeof value === 'string'\n    && ['sync-knowledge', 'recall-knowledge', 'graph-sync', 'open-dashboard'].includes(value);\n}","tryCatchPattern":null,"preventionTips":["Enumerate actions via buildControlPaneActions() or GET /api/snapshot rather than hardcoding ids in clients.","Validate client-supplied action ids through isControlPaneActionId before POSTing.","When renaming or removing an action, keep a deprecated alias for a release cycle."],"tags":["control-pane","validation","http","actions","routing"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}