ruvnet/ruflo · error · Error
Import file must contain a JSON object
Error message
Import file must contain a JSON object
What it means
ConfigFileManager.importFrom succeeds in JSON.parse but then checks the parsed value is a plain object (typeof === 'object', not null, not an array). A config file must be a JSON object at the top level; arrays, primitives, or null are rejected because setNestedValue and the rest of the manager assume an object root.
Source
Thrown at v3/@claude-flow/cli/src/services/config-file-manager.ts:164
const resolved = path.resolve(cwd, exportPath);
this.writeAtomic(resolved, config);
}
/** Import config from a specific path */
importFrom(cwd: string, importPath: string): void {
const resolved = path.resolve(cwd, importPath);
if (!fs.existsSync(resolved)) {
throw new Error(`Import file not found: ${resolved}`);
}
const content = fs.readFileSync(resolved, 'utf-8');
let imported: Record<string, unknown>;
try {
imported = JSON.parse(content);
} catch {
throw new Error(`Invalid JSON in import file: ${resolved}`);
}
if (typeof imported !== 'object' || imported === null || Array.isArray(imported)) {
throw new Error('Import file must contain a JSON object');
}
const targetPath = this.configPath ?? path.resolve(cwd, CONFIG_FILENAMES[0]);
this.writeAtomic(targetPath, imported);
this.config = imported;
this.configPath = targetPath;
}
/** Get the path to the current config file */
getConfigPath(): string | null {
return this.configPath;
}
/** Get default config */
getDefaults(): Record<string, unknown> {
return { ...DEFAULT_CONFIG };
}
/** Atomic write: write to .tmp then rename */View on GitHub (pinned to 6b01dc5a68)
Solutions
- Shape-check before importing: Array.isArray(parsed) || parsed === null || typeof parsed !== 'object'.
- Re-export from a working project so the file has an object root like {"version":...,"agents":...}.
- If you genuinely have an array, pick one element and write it as the single root object.
Example fix
// before — file contents: [ { "version": "3.5" } ]
manager.importFrom(cwd, './config.json'); // throws 'must contain a JSON object'
// after — file contents: { "version": "3.5", "agents": { ... } }
manager.importFrom(cwd, './config.json'); // ok Defensive patterns
Strategy: validation
Validate before calling
const raw = fs.readFileSync(path.resolve(cwd, importPath), 'utf-8');
const parsed = JSON.parse(raw);
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
throw new Error('import root must be a JSON object');
}
manager.importFrom(cwd, importPath); Type guard
function isJsonObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Prevention
- Ensure exported configs always have an object root.
- When templating configs, wrap the output in an object, not an array.
- Shape-check parsed imports before relying on them.
- Use isJsonObject() to narrow before setNestedValue-style access.
When it happens
Trigger: The import file parses to a JSON array (e.g. [...] ), to null, or to a primitive (a bare string/number/boolean). The file is valid JSON but the wrong shape.
Common situations: Exporting a list of configs instead of one object; the file contains just a quoted string; a templating tool rendered an array wrapper; someone replaced the config object with null.
Related errors
- Invalid JSON in import file: ${resolved}
- non-canonical number at ${path}
- Invalid completion type
- Routes config must be a flat array of routes
- Invalid route entry: ${JSON.stringify(r)}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/2de571016fb6f1aa.
Report an issue: GitHub.