musistudio/claude-code-router · error · Error
Provider manifest must be a JSON object.
Error message
Provider manifest must be a JSON object.
What it means
parseProviderManifestPayload expects the fetched manifest document to be a JSON object. If the parsed value is an array, a string, a number, null, or a primitive, isRecord(value) fails and this error is thrown before provider-field extraction.
Source
Thrown at packages/core/src/contracts/deep-link.ts:182
);
return {
...(account ? { account } : {}),
...(apiKey ? { apiKey } : {}),
baseUrl,
...(icon ? { icon } : {}),
...(modelDescriptions ? { modelDescriptions } : {}),
...(modelDisplayNames ? { modelDisplayNames } : {}),
...(modelMetadata ? { modelMetadata } : {}),
models,
...(name ? { name } : {}),
...(protocol ? { protocol } : {}),
...(source ? { source } : {})
};
}
export function parseProviderManifestPayload(value: unknown, sourceUrl?: string): ProviderDeepLinkPayload {
if (!isRecord(value)) {
throw new Error("Provider manifest must be a JSON object.");
}
const providerValue = isRecord(value.provider)
? value.provider
: isRecord(value.ccrProvider)
? value.ccrProvider
: value;
return parseProviderPayloadFields(new URLSearchParams(), providerValue, sourceUrl);
}
function parseProviderPayloadFields(
params: URLSearchParams,
payload: Record<string, unknown> | undefined,
sourceFallback?: string
): ProviderDeepLinkPayload {
const name = boundedString(
firstStringParam(params, ["name"]) ??
firstPayloadString(payload, ["name"]),
maxNameLength,View on GitHub (pinned to 99f24806c6)
Solutions
- Make the manifest a top-level JSON object (wrap arrays under a key, e.g. {"provider": {...}})
- Check what the manifest URL actually returns (curl it) and fix the document shape
- If the manifest legitimately uses ccrProvider key, keep it as {"ccrProvider": {...}} — still an object
Example fix
// before
[{"name":"acme","base_url":"https://api.acme.dev"}]
// after
{"provider":{"name":"acme","base_url":"https://api.acme.dev"}} Defensive patterns
Strategy: type-guard
Validate before calling
const parsed = JSON.parse(text); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return rejectManifest();
Type guard
const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === "object" && v !== null && !Array.isArray(v);
Try / catch
try { parseProviderManifestPayload(parsed); } catch (e) { if (e instanceof Error && e.message === "Provider manifest must be a JSON object.") return logManifestShape(parsed); throw e; } Prevention
- Validate fetched manifest bodies with isRecord before parsing
- Return objects (not arrays) from manifest endpoints
When it happens
Trigger: Calling parseProviderManifestPayload(value) where value is e.g. a JSON array of providers, a bare string, or the JSON.parse result of an error page body.
Common situations: Manifest endpoint returning a top-level array or a plain string; server returning an error/HTML body that was JSON.parse'd elsewhere; hand-authored manifest file with the provider nested but the file itself is a list.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Provider payload must be a JSON object.
- ${label} is too long.
- Provider manifest URL must use https.
- Provider manifest URL is invalid.
- Unsupported provider protocol: ${value}
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/426ff96c7a5b70d5.
Report an issue: GitHub.