{"record":{"id":"7071a4a13e1ec21e","repo":"affaan-m/ECC","slug":"unsupported-plan-canvas-request-path-url-pathname","errorCode":null,"errorMessage":"unsupported plan-canvas request path: ${url.pathname}","messagePattern":"unsupported plan-canvas request path: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/plan-canvas.js","lineNumber":110,"sourceCode":"\nfunction validatePort(port) {\n  const value = Number(port);\n  if (!Number.isInteger(value) || value < 0 || value > 65535) {\n    throw new Error(`invalid plan-canvas server port: ${port}`);\n  }\n  return value;\n}\n\nfunction validateRequestPath(requestPath) {\n  if (typeof requestPath !== 'string' || !requestPath.startsWith('/')) {\n    throw new Error('plan-canvas request path must be root-relative');\n  }\n  const url = new URL(requestPath, `http://${DEFAULT_HOST}`);\n  if (url.hostname !== DEFAULT_HOST) {\n    throw new Error('plan-canvas request path must stay on the loopback server');\n  }\n  if (!SAFE_REQUEST_PATHS.has(url.pathname) && !SESSION_REPLY_PATH.test(url.pathname)) {\n    throw new Error(`unsupported plan-canvas request path: ${url.pathname}`);\n  }\n  return `${url.pathname}${url.search}`;\n}\n\nfunction requestOptions(port, method, requestPath, headers) {\n  return {\n    host: DEFAULT_HOST,\n    port: validatePort(port),\n    method,\n    path: validateRequestPath(requestPath),\n    agent: false,\n    headers\n  };\n}\n\nfunction request(port, method, requestPath, body = null) {\n  return new Promise((resolve, reject) => {\n    const payload = body === null ? null : JSON.stringify(body);","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/plan-canvas.js#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use an endpoint listed in SAFE_REQUEST_PATHS at the top of scripts/plan-canvas.js (e.g. GET /api/sessions, POST /api/sessions).","For session replies, follow the SESSION_REPLY_PATH pattern exactly (correct session id shape).","Read the route table in the script to confirm the exact path spelling.","If a new endpoint is needed, add it both server-side and to SAFE_REQUEST_PATHS."],"exampleFix":"// before\nawait request(port, 'GET', '/api/session-list');\n// after\nawait request(port, 'GET', '/api/sessions');","handlingStrategy":"validation","validationCode":"const SAFE = new Set(['/api/health','/api/sessions','/api/open']);\nif (!SAFE.has(pathname) && !/^\\/api\\/sessions\\/[^/]+\\/reply$/.test(pathname)) {\n  throw new Error(`unsupported plan-canvas request path: ${pathname}`);\n}","typeGuard":"function isSafeRequestPath(p) {\n  try { const u = new URL(p, 'http://127.0.0.1');\n    return SAFE_REQUEST_PATHS.has(u.pathname) || SESSION_REPLY_PATH.test(u.pathname);\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  const res = await request(port, method, requestPath);\n} catch (err) {\n  if (err.message.startsWith('unsupported plan-canvas request path:')) {\n    console.error(`${err.message} — see SAFE_REQUEST_PATHS in scripts/plan-canvas.js`);\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Import/reuse the SAFE_REQUEST_PATHS constant rather than hardcoding routes","Check the server's route table when upgrading — endpoints can change between versions","Write tests that exercise each endpoint constant so renames break tests, not runtime","Match SESSION_REPLY_PATH exactly when constructing per-session URLs"],"tags":["http","api","validation"],"backgroundTag":"invalid-query-parameter","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}