decolua/9router · error · Error
Input must be a JSON object or array of objects
Error message
Input must be a JSON object or array of objects
What it means
parseAccountsInput in BulkImportGrokCliModal throws this after JSON.parse succeeds but the value is neither an object containing an `accounts` array nor a bare object. It guards the accepted bulk-import shapes for Grok CLI accounts.
Source
Thrown at src/app/(dashboard)/dashboard/providers/[id]/BulkImportGrokCliModal.js:52
fixed = fixed.replace(/\}\s*,\s*\{/g, "},{").replace(/\}\s*\{/g, "},{");
if (fixed.endsWith(",")) fixed = fixed.slice(0, -1);
fixed = `[${fixed}]`;
}
parsed = JSON.parse(fixed);
} catch {
throw initialErr;
}
}
if (Array.isArray(parsed)) {
return parsed;
}
if (parsed && typeof parsed === "object") {
if (Array.isArray(parsed.accounts)) return parsed.accounts;
return [parsed];
}
throw new Error("Input must be a JSON object or array of objects");
}
export default function BulkImportGrokCliModal({ isOpen, onClose, onSuccess }) {
const [jsonText, setJsonText] = useState("");
const [submitting, setSubmitting] = useState(false);
const [parseError, setParseError] = useState("");
const [result, setResult] = useState(null);
const [isDragging, setIsDragging] = useState(false);
const [fileCountInfo, setFileCountInfo] = useState(null);
const fileInputRef = useRef(null);
const handleClose = () => {
if (submitting) return;
setJsonText("");
setParseError("");
setResult(null);
setFileCountInfo(null);
setIsDragging(false);View on GitHub (pinned to 90b52e06ff)
Solutions
- Wrap the account object in an array: [{...}] or {"accounts": [...]} Remove surrounding quotes if the whole input is a JSON-encoded string If importing JSONL (one object per line), convert to a JSON array first Trim trailing commas/comments that make JSON.parse produce unexpected types
Example fix
// before
throw new Error("Input must be a JSON object or array of objects");
// after — accept arrays at top level too
if (Array.isArray(parsed)) return parsed;
if (parsed && typeof parsed === "object") {
if (Array.isArray(parsed.accounts)) return parsed.accounts;
return [parsed];
}
throw new Error("Input must be a JSON object or array of objects"); Defensive patterns
Strategy: validation
Validate before calling
function parseAccountsInput(text) {
const parsed = JSON.parse(text);
if (Array.isArray(parsed)) return parsed;
if (parsed && typeof parsed === "object" && Array.isArray(parsed.accounts)) return parsed.accounts;
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return [parsed];
throw new Error("Input must be a JSON object or array of objects");
} Type guard
function isAccountShape(v) {
return v !== null && typeof v === "object" && !Array.isArray(v);
} Try / catch
try {
const accounts = parseAccountsInput(jsonText);
setParseError("");
await submit(accounts);
} catch (err) {
setParseError(err.message);
} Prevention
- Paste a top-level array [{...}] or an object with an accounts key
- Avoid pasting a JSON-encoded string (double-encoded) — check for leading quotes
- Convert JSONL input into a single JSON array first
- Preview parsed account count in the UI before submit
When it happens
Trigger: Pasting JSON that parses to a primitive (string, number, true/false/null) — parsed is truthy but typeof !== 'object'; note `null` passes JSON.parse but typeof null === 'object' with Array.isArray(null) false, so [null] path is not hit and null itself reaches... actually null is excluded earlier by `parsed &&`, so the throw fires for any truthy non-object or when an empty/odd top-level shape arrives.
Common situations: User pastes a quoted JSON string ("…" parses to a string), a bare number, `true`, or a JSONL file with multiple lines that isn't valid single-document JSON; copies the inner value of an export instead of the wrapper object.
Related errors
- CLIProxyAPI auth JSON is invalid
- Invalid host:port:user:pass format
- Unsupported format
- Item is not an object
- Missing accessToken
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/1f38ea51020deb33.
Report an issue: GitHub.