nexu-io/open-design · error
invalid JSON in ${filePath}: ${message}
Error message
invalid JSON in ${filePath}: ${message} What it means
Thrown by readJsonObject() in the artifacts CLI when JSON.parse fails on the file pointed to by --manifest. The daemon never sees this; it is a local CLI-side parse guard that fires before any HTTP request is made. The original parser message is appended so the syntax error is visible.
Source
Thrown at apps/daemon/src/artifacts-cli.ts:98
options.encoding = value;
} else if (arg === '-h' || arg === '--help') {
options.help = true;
} else {
return { error: `unknown option: ${arg}` };
}
}
return options;
}
async function readJsonObject(filePath: string): Promise<JsonObject> {
const text = await readFile(filePath, 'utf8');
let value: unknown;
try {
value = JSON.parse(text) as unknown;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`invalid JSON in ${filePath}: ${message}`);
}
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${filePath} must contain a JSON object`);
}
return value as JsonObject;
}
export async function runArtifactsCli(args: string[]): Promise<ArtifactCliResult> {
const options = parseOptions(args);
if ('error' in options) return fail(options.error);
if (options.help || !options.command) {
process.stdout.write(USAGE);
return { exitCode: options.command ? 0 : 1 };
}
if (options.command !== 'create') return fail(`unknown artifacts command: ${options.command}`);
if (!options.name) return fail('create requires --name <path>');
if (!options.inputPath) return fail('create requires --input <file>');
View on GitHub (pinned to 5be4028344)
Solutions
- Validate the file with a JSON linter or `node -e "JSON.parse(require('fs').readFileSync('<path>','utf8'))"` and fix the reported syntax position.
- Re-save the file as plain UTF-8 without BOM and with double-quoted keys.
- If you intended to allow comments/trailing commas, switch to strict JSON; the parser does not accept JSONC.
- Check the appended ${message} in the error — it names the exact byte offset of the parse failure.
Example fix
// before: manifest.json
{ kind: "html", renderer: "html", exports: ["html"] }
// after: valid JSON (quoted keys)
{ "kind": "html", "renderer": "html", "exports": ["html"] } Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
function assertValidManifestFile(p: string) {
const text = readFileSync(p, 'utf8');
let v: unknown;
try { v = JSON.parse(text); } catch (e) {
throw new Error(`manifest ${p} is not valid JSON: ${(e as Error).message}`);
}
if (!v || typeof v !== 'object' || Array.isArray(v)) {
throw new Error(`manifest ${p} must be a JSON object`);
}
}
assertValidManifestFile(manifestPath); Type guard
function isJsonObject(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object' && !Array.isArray(value);
} Try / catch
try {
const manifest = await readJsonObject(manifestPath);
} catch (e) {
console.error((e as Error).message); // already includes the parse offset
process.exit(1);
} Prevention
- Run files through a JSON linter in your editor/CI before invoking the CLI.
- Save manifest files as UTF-8 without BOM.
- Treat the appended ${message} as the source of truth for the syntax-error position.
When it happens
Trigger: Running `od artifacts create --manifest <path>` where <path> is missing a comma, has a trailing comma, an unquoted key, BOM, or any other JSON syntax error. readJsonObject reads the file as utf8 and calls JSON.parse on the contents.
Common situations: Hand-editing a manifest JSON and leaving a syntax error; saving with an editor that inserts a UTF-8 BOM; copy-pasting a manifest from docs that used single quotes; CRLF/inline comments (JSONC) mistakenly treated as JSON.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${filePath} must contain a JSON object
- ARTIFACT_MANIFEST_INVALID
- ARTIFACT_MANIFEST_REQUIRED
- ARTIFACT_PUBLICATION_BLOCKED
- proposal patch.after is not valid JSON
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/6e59a4d9e9db928a.
Report an issue: GitHub.