vercel/ai · error
Invalid Pi session file name: ${input.sessionFileName}
Error message
Invalid Pi session file name: ${input.sessionFileName} What it means
resolveContainedHostPath validates that the Pi session file name resolves to a path strictly contained in the host's local session mirror directory. The name must be a safe basename ending in .jsonl or .json and must not escape the base dir. It throws for empty paths, absolute paths, parent escapes ('..'), or names failing the safe-file-name pattern.
Source
Thrown at packages/harness-pi/src/pi-resume-state.ts:78
return privateSessionDir;
}
function resolveContainedHostPath(input: {
readonly baseDir: string;
readonly sessionFileName: string;
}): string {
const baseDir = path.resolve(input.baseDir);
const filePath = path.resolve(
baseDir,
safePiSessionFileName(input.sessionFileName),
);
const relativePath = path.relative(baseDir, filePath);
if (
relativePath === '' ||
relativePath.startsWith('..') ||
path.isAbsolute(relativePath)
) {
throw new Error(`Invalid Pi session file name: ${input.sessionFileName}`);
}
return filePath;
}
function resolveContainedSandboxPath(input: {
readonly privateSessionDir: string;
readonly sessionFileName: string;
}): string {
const sessionDir = path.posix.resolve(input.privateSessionDir);
const filePath = path.posix.resolve(
sessionDir,
safePiSessionFileName(input.sessionFileName),
);
const relativePath = path.posix.relative(sessionDir, filePath);
if (
relativePath === '' ||
relativePath.startsWith('..') ||
path.posix.isAbsolute(relativePath)View on GitHub (pinned to 69428b1f8b)
Solutions
- Use only basenames matching /^[A-Za-z0-9][A-Za-z0-9._-]*\.jsonl?$/ for sessionFileName
- Read sessionFileName from piResumeStateSchema-validated state (it enforces the same pattern)
- Strip any directory components from the stored name before calling resume APIs
Example fix
// before
await persistSessionFileToSandbox({
...
sessionFileName: '/sessions/abc.jsonl', // absolute path -> throws
});
// after
await persistSessionFileToSandbox({
...
sessionFileName: 'abc.jsonl', // safe basename
}); Defensive patterns
Strategy: validation
Validate before calling
const SAFE = /^[A-Za-z0-9][A-Za-z0-9._-]*\.jsonl?$/;
export function isValidPiSessionFileName(name: unknown): name is string {
return typeof name === 'string' && SAFE.test(name);
} Type guard
export function isPiResumeState(v: unknown): v is { sessionFileName?: string } {
return typeof v === 'object' && v !== null &&
(!('sessionFileName' in v) || isValidPiSessionFileName((v as any).sessionFileName));
} Try / catch
try {
await persistSessionFileToSandbox({ ...args });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid Pi session file name')) {
// reject/repair the stored sessionFileName
}
throw e;
} Prevention
- Always read sessionFileName from piResumeStateSchema-validated state
- Store basenames only, never absolute or relative directory paths
- Add the regex check at the boundary where state is loaded from disk
When it happens
Trigger: persistSessionFileToSandbox or pullSessionFileFromSandbox receives a sessionFileName that is absolute, empty, contains path traversal ('../'), or is not a `<safe-name>.jsonl`/`.json` basename — typically because it came from a corrupted or tampered resume state's `data` payload.
Common situations: Hand-editing session state to use a full path instead of a basename; restoring state written by another harness version; a hostile/legacy sessionFileName like '../../etc/passwd' or 'session.txt'.
Related errors
- Invalid Cline history file name: ${historyFileName}
- Invalid Cline history file name: ${input.historyFileName}
- Invalid Pi ${label} name: ${name}
- Invalid skill file path for ${skillName}: ${filePath}
- Invalid argument for parameter model: model ${model.provider
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/12c94b337d42d3f1.
Report an issue: GitHub.