pbakaus/impeccable · error · Error
invalid session id: ${id}
Error message
invalid session id: ${id} What it means
Thrown by safeSessionId() in skill/scripts/lib/impeccable-paths.mjs when an id is not a string matching ^[A-Za-z0-9_-]{1,128}$. Session ids become path segments (journals, snapshots, accept receipts, preview manifests, generated component dirs) and arrive from CLI --id args and HTTP payloads, so anything containing a path separator or `..` must be rejected before path.join can escape `.impeccable/live/`. Real ids are 8 hex chars; tests use short slugs.
Source
Thrown at skill/scripts/lib/impeccable-paths.mjs:110
return filePath;
}
export function removeLiveServerInfo(cwd = process.cwd(), options = {}) {
for (const filePath of [getLiveServerPath(cwd, options), getLegacyLiveServerPath(cwd, options)]) {
try { fs.unlinkSync(filePath); } catch {}
}
}
/**
* Session IDs become path segments (journals, snapshots, accept receipts,
* preview manifests, generated component dirs). They arrive from CLI `--id`
* arguments and HTTP payloads, so anything containing a separator or `..` must
* be rejected before it reaches path.join, which would happily escape
* `.impeccable/live/`. Real IDs are 8 hex chars; the tests use short slugs.
*/
export function safeSessionId(id) {
if (typeof id !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(id)) {
throw new Error('invalid session id: ' + id);
}
return id;
}
export function getLiveSessionsDir(cwd = process.cwd(), options = {}) {
return path.join(getLiveDir(cwd, options), 'sessions');
}
export function getLegacyLiveSessionsDir(cwd = process.cwd(), options = {}) {
return path.join(resolveProjectRoot(cwd, options), '.impeccable-live', 'sessions');
}
export function getLiveAnnotationsDir(cwd = process.cwd(), options = {}) {
return path.join(getLiveDir(cwd, options), 'annotations');
}
export function getCritiqueDir(cwd = process.cwd(), options = {}) {
return path.join(getImpeccableDir(cwd, options), CRITIQUE_DIR);View on GitHub (pinned to d14711ae3d)
Solutions
- Generate ids from 8 hex chars (e.g. crypto.randomBytes(4).toString('hex')) to match the real-session shape.
- Strip/replace any character outside [A-Za-z0-9_-] before passing the id to the API, or reject it.
- If you control the caller, type-check the id is a string and within length before invoking any live-session function.
Example fix
// before
const id = req.query.id; // "../../etc/passwd"
useSession(id);
// after
import { safeSessionId } from './impeccable-paths.mjs';
const id = safeSessionId(req.query.id); // throws on traversal Defensive patterns
Strategy: type-guard
Validate before calling
const SESSION_RE = /^[A-Za-z0-9_-]{1,128}$/;
function safeId(id) {
if (typeof id !== 'string' || !SESSION_RE.test(id)) {
throw new Error('invalid session id: ' + id);
}
return id;
} Type guard
function isSafeSessionId(id) {
return typeof id === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(id);
} Try / catch
// At the HTTP/CLI boundary, validate before any path operation.
if (!isSafeSessionId(req.params.id)) {
return res.status(400).json({ error: 'invalid session id' });
} Prevention
- Generate session ids from crypto.randomBytes(4).toString('hex').
- Never feed user-supplied strings straight into path.join.
- Treat session ids as untrusted at every trust boundary.
When it happens
Trigger: Passing a session id containing `/`, `\`, `..`, spaces, or other punctuation; an id longer than 128 chars; or a non-string (number, null) from an HTTP payload or unvalidated CLI arg. Any of these reaches safeSessionId before being used in a filesystem path.
Common situations: A client sending a crafted or malformed id over the live HTTP API; a script passing a filename or URL as --id; a generated id that accidentally includes a slash; id coming from an untyped JSON field.
Related errors
- invalid session id: ${id}
- Invalid svelte-component source file
- Svelte-component source file escapes project root
- invalid catalog: ${validation.errors.join('; ')}
- concept-seed: --scope must be direction or surface
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/dc8f6c1efceb94b8.
Report an issue: GitHub.