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

  1. 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

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


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/1f38ea51020deb33. Report an issue: GitHub.