pbakaus/impeccable · error · Error
invalid session id
Error message
invalid session id: ${id} What it means
Thrown by safeSessionId when a session id fails the pattern ^[A-Za-z0-9_-]{1,128}$ or is not a string. Session ids become path segments under .impeccable/live/ (journals, snapshots, accept receipts, preview manifests, generated component dirs) and arrive from untrusted surfaces — CLI --id arguments and HTTP payloads — so separators, '..', or over-long ids must be rejected before path.join can escape the sessions directory. Genuine ids are 8 hex characters; tests use short slugs.
Solutions
- Use the exact id printed when the session was created (8 hex chars).
- If generating ids yourself, stick to [A-Za-z0-9_-] and at most 128 chars — e.g. crypto.randomBytes(4).toString('hex').
- Send ids as strings in HTTP payloads, never numbers or objects.
- Remove any path separators, dots, or whitespace before passing the id.
Example fix
// before
const id = req.body.id; // 12345 (number) or "../oops"
const dir = path.join(sessionsDir, id); // traversal / NaN-ish join
// after
import { safeSessionId } from './lib/impeccable-paths.mjs';
const id = safeSessionId(String(req.body.id)); // throws early on bad input
const dir = path.join(sessionsDir, id); Defensive patterns
Strategy: type-guard
Validate before calling
const SESSION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
function isValidSessionId(id) {
return typeof id === 'string' && SESSION_ID_RE.test(id);
}
// gate untrusted input (CLI --id, HTTP payload) before any path work:
if (!isValidSessionId(inputId)) return res.status(400).json({ error: 'invalid session id' });
const dir = path.join(sessionsDir, safeSessionId(inputId)); Type guard
import { safeSessionId } from './lib/impeccable-paths.mjs';
function asSessionId(id) {
try { return safeSessionId(id); } catch { return null; }
}
const id = asSessionId(req.body?.id);
if (id === null) { /* reject request */ } Try / catch
try {
const id = safeSessionId(raw);
} catch (err) {
if (/^invalid session id/.test(err.message)) {
// untrusted input problem: reject at the trust boundary, never retry or sanitize by stripping characters
return badRequest('session id must match [A-Za-z0-9_-]{1,128}');
}
throw err;
} Prevention
- Always use the id emitted when the session was created (8 hex chars).
- Generate ids with crypto.randomBytes(4).toString('hex') — already in-alphabet.
- Coerce HTTP payload ids with String() and validate before joining them into paths.
- Never sanitize a bad id by deleting characters; reject it — partial sanitizing can still yield a wrong session.
When it happens
Trigger: Passing `--id ../../etc` or `--id foo/bar` (path separators); an id with spaces or unicode punctuation from a copy-paste; `--id` with an undefined/empty value interpolated into a request payload; an HTTP client posting {"id": 12345} as a number rather than a string.
Common situations: Hand-typed ids drifting from the 8-hex-char convention; agents fabricating ids instead of reading them from session output; API consumers assuming any unique string is acceptable; attempt (accidental or deliberate) to traverse out of the live-sessions directory.
Related errors
- Available commands
- build-phase: finish --disposition…
- comp-spec: --background must be transparent, opaque, or…
- comp-spec: cannot read regions
- embed-prompt: malformed PNG
AI-assisted analysis of pbakaus/impeccable@f88b2837a7 (2026-08-18).
Data as JSON: /api/errors/dc8f6c1efceb94b8.
Report an issue: GitHub.
Appendix: 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 f88b2837a7)