garrytan/gstack · error · Error
pdf: --from-file ${payloadPath} must be a JSON object, got $
Error message
pdf: --from-file ${payloadPath} must be a JSON object, got ${Array.isArray(json) ? 'array' : typeof json}. What it means
Thrown by parsePdfFromFile() when the parsed JSON is not a plain object — it is null, an array, or a primitive type (string, number, boolean). The PDF config must be a JSON object whose keys map to ParsedPdfArgs fields (output, format, width, etc.).
Source
Thrown at browse/src/meta-commands.ts:161
// must pass validateReadPath so the safe-dirs policy can't be skirted
// by routing reads through the --from-file shortcut.
try {
validateReadPath(path.resolve(payloadPath));
} catch {
throw new Error(
`pdf: --from-file ${payloadPath} must be under ${SAFE_DIRECTORIES.join(' or ')} (security policy). Copy the payload into the project tree or /tmp first.`
);
}
const raw = fs.readFileSync(payloadPath, 'utf8');
let json: any;
try {
json = JSON.parse(raw);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`pdf: --from-file ${payloadPath} is not valid JSON (${msg}).`);
}
if (json === null || typeof json !== 'object' || Array.isArray(json)) {
throw new Error(`pdf: --from-file ${payloadPath} must be a JSON object, got ${Array.isArray(json) ? 'array' : typeof json}.`);
}
const out: ParsedPdfArgs = {
output: json.output || `${TEMP_DIR}/browse-page.pdf`,
format: json.format,
width: json.width,
height: json.height,
marginTop: json.marginTop,
marginRight: json.marginRight,
marginBottom: json.marginBottom,
marginLeft: json.marginLeft,
headerTemplate: json.headerTemplate,
footerTemplate: json.footerTemplate,
pageNumbers: json.pageNumbers === true,
tagged: json.tagged === true,
outline: json.outline === true,
printBackground: json.printBackground === true,
preferCSSPageSize: json.preferCSSPageSize === true,
toc: json.toc === true,View on GitHub (pinned to 94993f7401)
Solutions
- Ensure the JSON file's top-level value is an object ({...}), not an array ([...]) or primitive
- Wrap any array content inside an object if the config needs list data
Example fix
// before — array at top level
[
{ "format": "A4" },
{ "format": "Letter" }
]
// after — single object
{
"format": "A4",
"printBackground": true
} Defensive patterns
Strategy: type-guard
Validate before calling
const raw = fs.readFileSync(payloadPath, 'utf8');
const json = JSON.parse(raw);
if (json === null || typeof json !== 'object' || Array.isArray(json)) {
throw new Error('Payload must be a JSON object');
} Type guard
function isPdfConfigObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Prevention
- Ensure the JSON file's top-level value is {...}, not [...] or a primitive
- Use a JSON schema validator to check structure before passing to --from-file
When it happens
Trigger: The --from-file JSON file parses successfully but the top-level value is an array (e.g., '[...]'), null, or a primitive like a bare string or number.
Common situations: User wraps the config in an array instead of an object, or the file contains a bare JSON value from a different tool's output format.
Related errors
- pdf: --from-file ${payloadPath} is not valid JSON (${msg}).
- excalidraw scene has no elements array
- Skill name is empty.
- Invalid skill name "${name}". Must be lowercase letters/digi
- stageSkill: files map is empty.
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/6882aa561b110c4d.
Report an issue: GitHub.