affaan-m/ECC · error · Error

unsupported plan-canvas request path

Error message

unsupported plan-canvas request path: ${url.pathname}

What it means

The request path must resolve to either one of SAFE_REQUEST_PATHS (the fixed API endpoints like /api/sessions, /api/health) or match SESSION_REPLY_PATH (per-session reply routes). Any other pathname is rejected to keep the local server surface minimal.

Solutions

  1. Use an endpoint listed in SAFE_REQUEST_PATHS at the top of scripts/plan-canvas.js (e.g. GET /api/sessions, POST /api/sessions).
  2. For session replies, follow the SESSION_REPLY_PATH pattern exactly (correct session id shape).
  3. Read the route table in the script to confirm the exact path spelling.
  4. If a new endpoint is needed, add it both server-side and to SAFE_REQUEST_PATHS.

Example fix

// before
await request(port, 'GET', '/api/session-list');
// after
await request(port, 'GET', '/api/sessions');
Defensive patterns

Strategy: validation

Validate before calling

const SAFE = new Set(['/api/health','/api/sessions','/api/open']);
if (!SAFE.has(pathname) && !/^\/api\/sessions\/[^/]+\/reply$/.test(pathname)) {
  throw new Error(`unsupported plan-canvas request path: ${pathname}`);
}

Type guard

function isSafeRequestPath(p) {
  try { const u = new URL(p, 'http://127.0.0.1');
    return SAFE_REQUEST_PATHS.has(u.pathname) || SESSION_REPLY_PATH.test(u.pathname);
  } catch { return false; }
}

Try / catch

try {
  const res = await request(port, method, requestPath);
} catch (err) {
  if (err.message.startsWith('unsupported plan-canvas request path:')) {
    console.error(`${err.message} — see SAFE_REQUEST_PATHS in scripts/plan-canvas.js`);
    process.exit(2);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the request helper with a made-up endpoint such as '/api/v2/sessions', a typo like '/api/session', or a deleted route — the `!SAFE_REQUEST_PATHS.has(url.pathname) && !SESSION_REPLY_PATH.test(url.pathname)` condition becomes true.

Common situations: Guessing API routes from REST conventions; version drift after the server endpoints changed; typos in singular/plural route names; tooling constructing reply URLs that don't match the expected session-id pattern.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at scripts/plan-canvas.js:110

function validatePort(port) {
  const value = Number(port);
  if (!Number.isInteger(value) || value < 0 || value > 65535) {
    throw new Error(`invalid plan-canvas server port: ${port}`);
  }
  return value;
}

function validateRequestPath(requestPath) {
  if (typeof requestPath !== 'string' || !requestPath.startsWith('/')) {
    throw new Error('plan-canvas request path must be root-relative');
  }
  const url = new URL(requestPath, `http://${DEFAULT_HOST}`);
  if (url.hostname !== DEFAULT_HOST) {
    throw new Error('plan-canvas request path must stay on the loopback server');
  }
  if (!SAFE_REQUEST_PATHS.has(url.pathname) && !SESSION_REPLY_PATH.test(url.pathname)) {
    throw new Error(`unsupported plan-canvas request path: ${url.pathname}`);
  }
  return `${url.pathname}${url.search}`;
}

function requestOptions(port, method, requestPath, headers) {
  return {
    host: DEFAULT_HOST,
    port: validatePort(port),
    method,
    path: validateRequestPath(requestPath),
    agent: false,
    headers
  };
}

function request(port, method, requestPath, body = null) {
  return new Promise((resolve, reject) => {
    const payload = body === null ? null : JSON.stringify(body);

View on GitHub (pinned to 8321021c54)