affaan-m/ECC · error · Error

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

Error message

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

What it means

Thrown by validateRequestPath in scripts/plan-canvas.js when the parsed pathname is not in the SAFE_REQUEST_PATHS allowlist and does not match the SESSION_REPLY_PATH regex. This is a path-allowlist guard: only known endpoints (/, /health, /shutdown, /api/await, /api/sessions, /api/end, and /api/session/<12-hex-key>/(reply|typing)) may be requested by the CLI.

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 01e15490f0)

Solutions

  1. If adding a new server endpoint, also add its pathname to SAFE_REQUEST_PATHS or extend SESSION_REPLY_PATH.
  2. For session-scoped paths, ensure the key is a 12-character lowercase hex string and the suffix is /reply or /typing.
  3. End users: this indicates a CLI bug — update ECC or report the issue.

Example fix

// before (internal call, path not allowlisted)
request(port, 'GET', '/api/unknown');
// after
request(port, 'GET', '/api/sessions');
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_PATHS = new Set(['/', '/health', '/shutdown', '/api/await', '/api/sessions', '/api/end']);
const SESSION_PATH = /^\/api\/session\/[a-f0-9]{12}\/(reply|typing)$/;
function assertAllowedPath(pathname) {
  if (!SAFE_PATHS.has(pathname) && !SESSION_PATH.test(pathname)) {
    throw new Error(`Refusing unsupported request path: ${pathname}`);
  }
}

Prevention

When it happens

Trigger: An internal call to a path the server does not expose, or a typo in a pathname constant within plan-canvas.js. The session reply/typing paths require exactly 12 lowercase hex characters in the key segment, followed by /reply or /typing.

Common situations: A new endpoint added to the server but not to SAFE_REQUEST_PATHS; a refactored path string that drops a segment; an invalid session key length in the path that fails the regex.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/7071a4a13e1ec21e. Report an issue: GitHub.