{"record":{"id":"c808e93b5822962b","repo":"affaan-m/ECC","slug":"invalid-plan-canvas-session-key","errorCode":null,"errorMessage":"invalid plan-canvas session key","messagePattern":"invalid plan-canvas session key","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/plan-canvas.js","lineNumber":246,"sourceCode":"  const res = await request(port, 'POST', '/api/sessions', {\n    file: path.resolve(file),\n    reopen: args.includes('--reopen')\n  });\n  if (res.statusCode === 409) return res.body;\n  if (res.statusCode !== 200) throw new Error(res.body.error || `open failed (HTTP ${res.statusCode})`);\n  const url = `http://${DEFAULT_HOST}:${port}${res.body.url}`;\n  const launched = args.includes('--no-open') ? false : openBrowser(url);\n  return {\n    status: 'open',\n    url,\n    browser: launched ? 'opened' : 'not opened',\n    next_step:\n      'Run `ecc-plan-canvas await <file>` and leave it running; it returns when the human sends feedback, a verdict, or ends the session.'\n  };\n}\n\nfunction awaitRequest(port, key, timeoutMs) {\n  if (!/^[a-f0-9]{12}$/.test(key)) throw new Error('invalid plan-canvas session key');\n  const params = new URLSearchParams({ key });\n  if (timeoutMs !== null) params.set('timeoutMs', String(timeoutMs));\n  return new Promise((resolve, reject) => {\n    const req = http.request(\n      requestOptions(port, 'GET', `/api/await?${params}`, {}),\n      res => {\n        let data = '';\n        res.on('data', chunk => {\n          data += chunk;\n        });\n        res.on('end', () => {\n          try {\n            resolve(JSON.parse(data.trim()));\n          } catch {\n            reject(new Error('await response was not JSON (server restarted?) - re-run await; feedback is never lost'));\n          }\n        });\n      }","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/plan-canvas.js#L228-L264","documentation":"awaitRequest polls the canvas server's /api/await endpoint using a session key that must be exactly 12 lowercase hex characters (a 12-char server-generated session id). Before any network work, the key is validated with the regex /^[a-f0-9]{12}$/; anything else — empty, uppercase, wrong length, or containing separators — throws 'invalid plan-canvas session key' immediately, protecting the URL from malformed or injected keys.","triggerScenarios":"Calling `ecc-plan-canvas await <file>` (via result/awaitRequest) when the derived session key is not a 12-char hex string: the key was truncated or hand-edited, the session lookup (sessionKeyFor / stored state) returned undefined or an old-format key, or a caller passed the file path itself instead of the key.","commonSituations":"Scripting the CLI and pasting a key from an old session after the state dir was cleared (so the lookup falls back to garbage); copying a key with surrounding whitespace or quotes into a shell variable; mixing keys between two canvas state dirs (dev vs prod); a schema change in an ECC upgrade that changed key length while old scripts cache keys.","solutions":["Print/log the key right before the call and confirm it is 12 lowercase hex chars; trim whitespace and quotes from shell variables.","Re-derive the key from the current state dir (the file that `open` registered) instead of reusing a cached or hand-copied key.","If the state dir was cleared or migrated, rerun `ecc-plan-canvas open <file>` to mint a fresh session key, then `await` with that key.","If you hardcode the key in a script, fetch it programmatically from the session state instead of a literal so version/format changes cannot break it."],"exampleFix":"// before (stale/unknown format key)\nawait awaitRequest(port, process.env.SESSION_KEY, timeout);\n\n// after (validate before calling)\nconst key = (process.env.SESSION_KEY || '').trim().toLowerCase();\nif (!/^[a-f0-9]{12}$/.test(key)) {\n  throw new Error(`bad session key \"${key}\" — rerun ecc-plan-canvas open to get a fresh one`);\n}\nawait awaitRequest(port, key, timeout);","handlingStrategy":"validation","validationCode":"if (!/^[a-f0-9]{12}$/.test(key)) throw new Error(`invalid session key: ${JSON.stringify(key)}`);","typeGuard":"const isSessionKey = (v) => typeof v === 'string' && /^[a-f0-9]{12}$/.test(v);","tryCatchPattern":"try {\n  return await awaitRequest(port, key, timeoutMs);\n} catch (err) {\n  if (/invalid plan-canvas session key/.test(err.message)) {\n    // re-derive key from state dir or rerun `open`\n  }\n  throw err;\n}","preventionTips":["Never hand-edit or truncate session keys; always read them from session state.","Trim whitespace/quotes when keys travel through shell variables.","Rerun `open` after clearing or switching state dirs so keys match the active server.","Validate keys with the same 12-hex regex the CLI uses before any network call."],"tags":["validation","identifier","cli","plan-canvas"],"backgroundTag":"invalid-identifier-format","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"}