musistudio/claude-code-router · error · Error
Claude Design profiles can only be opened from CCR Desktop.
Error message
Claude Design profiles can only be opened from CCR Desktop.
What it means
Thrown when the account endpoint declares JSON (or sniffs as JSON) but JSON.parse fails on the body. The transport and content type checks passed; the payload itself is syntactically invalid JSON.
Source
Thrown at packages/cli/src/cli.ts:125
const profileOptions = options as ProfileCliOptions;
if (profileOptions.help || !profileOptions.profileRef) {
printHelp(profileOptions.help ? 0 : 2);
return;
}
const configDir = CONFIGDIR;
const config = await loadAppConfig();
assertAvailableGatewayModels(config);
await applyProfileConfig(config);
const profile = findProfileForOpen(config, profileOptions.profileRef);
const surface = profileOptions.surface ?? defaultProfileOpenSurface(profile);
const resolvedSurface = resolveProfileOpenSurface(profile, surface);
if (profile.agent === "zcode" && profileOptions.agentArgs.length > 0) {
throw new Error("ZCode profiles can only open the app; agent arguments are not supported.");
}
if (profile.agent === "claude-design") {
throw new Error("Claude Design profiles can only be opened from CCR Desktop.");
}
if (profile.agent === "claude-code" && resolvedSurface === "app" && profileOptions.agentArgs.length > 0) {
throw new Error("Claude App profiles do not support agent arguments.");
}
if (profile.agent === "codex" && resolvedSurface === "app" && profileOptions.agentArgs.length === 0) {
const state = await startService({
command: "start",
daemonChild: false,
ensureGatewayRunning: true,
help: false,
open: false,
profileManaged: false,
startGateway: true
});
const opened = await callServiceRpc<ProfileOpenResult>(state, "openProfile", [{ profileId: profile.id, surface: "app" }], 30_000);
process.stdout.write(`${opened.message}\n`);
return;
}View on GitHub (pinned to 99f24806c6)
Solutions
- Log/inspect the raw text to find where JSON syntax breaks (truncation, wrapper, BOM)
- Fix server-side serialization or proxy buffering causing truncation
- If the endpoint wraps JSON (JSONP, )]}', prefix), strip the wrapper before parsing or point at a clean JSON endpoint
- Retry once — transient truncation from flaky proxies is common
Defensive patterns
Strategy: try-catch
Validate before calling
function isParsableJson(text: string): boolean {
try { JSON.parse(text); return true; } catch { return false; }
}
if (!isParsableJson(result.text ?? '')) { /* handle before calling */ } Type guard
function isValidJsonPayload(text: string): text is string {
try { JSON.parse(text); return true; } catch { return false; }
} Try / catch
try {
return await parseWebContentFetchResult(result);
} catch (error) {
if (error instanceof Error && error.message.includes('malformed JSON')) {
return await retryFetchOnce(); // transient truncation
}
throw error;
} Prevention
- Retry once on malformed JSON — proxies often truncate transiently
- Sniff for wrappers (BOM, JSONP, log prefixes) before parsing
- Compare Content-Length against actual body length when available
When it happens
Trigger: result.ok is true, responseLooksJson returns true (contentType includes 'json' or body looks like JSON), but JSON.parse(text) throws — e.g. truncated response, JSONP callback wrapper, BOM/prefix garbage, or a partially streamed body.
Common situations: Truncated response due to proxy timeouts or content-length mismatch; server concatenating multiple JSON objects; encoding issues (UTF-16, BOM); debug endpoints wrapping JSON in logs; CDN edge caching a corrupted response.
Related errors
- ZCode profiles can only open the app; agent arguments are no
- HTTP + response.status + from CCR remote sync
- The CCR artifact endpoint returned HTTP ${response.status}.
- Provider manifest must be a JSON object.
- Provider payload must be a JSON object.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/ca8e74a21a8fec99.
Report an issue: GitHub.