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
- If adding a new server endpoint, also add its pathname to SAFE_REQUEST_PATHS or extend SESSION_REPLY_PATH.
- For session-scoped paths, ensure the key is a 12-character lowercase hex string and the suffix is /reply or /typing.
- 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 adding a server endpoint, update SAFE_REQUEST_PATHS in the same change.
- Keep session-keyed paths to the documented /api/session/<key>/(reply|typing) shape.
- End users hitting this should treat it as a CLI bug and report it.
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
- plan-canvas request path must stay on the loopback server
- Missing value for --target
- Unknown catalog command: ${options.command}
- Failed to read ${path.basename(filePath)}: ${error.message}
- invalid plan-canvas server port: ${port}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/7071a4a13e1ec21e.
Report an issue: GitHub.