musistudio/claude-code-router · error · Error

ZCode profiles can only open the app; agent arguments are no

Error message

ZCode profiles can only open the app; agent arguments are not supported.

What it means

Thrown by parseWebContentFetchResult when the provider's account endpoint responds 2xx but the Content-Type (or body sniffing) does not look like JSON. This is part of the OAuth/account-linking flow where an expected JSON account info endpoint instead returns HTML or another content type.

Source

Thrown at packages/cli/src/cli.ts:122

    await runWebServer(options);
    return;
  }

  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);

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Verify the account endpoint URL actually returns JSON (curl -i it and check Content-Type)
  2. If a proxy/portal intercepts the request, fix network config or add required auth headers/cookies
  3. If the endpoint legitimately returns non-JSON, correct the configured account URL in the provider settings
  4. Check response body via readableResponseSnippet path/logging to see what was actually returned (HTML login page, XML, empty)

Example fix

// before
accountUrl: "https://provider.example.com/account" // returns HTML
// after
accountUrl: "https://provider.example.com/api/user" // returns application/json
Defensive patterns

Strategy: try-catch

Validate before calling

const looksJson = (ct?: string, text?: string) =>
  (ct ?? '').toLowerCase().includes('json') || /^\s*[\[{]/.test(text ?? '');

if (!result.ok || !looksJson(result.contentType, result.text)) {
  // fall back to UI-driven account linking instead of parsing
}

Type guard

function isJsonFetchResult(result: FetchResult): boolean {
  return result.ok && responseLooksJson(result.contentType ?? '',
    typeof result.text === 'string' ? result.text : '');
}

Try / catch

try {
  const data = await parseWebContentFetchResult(result);
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Account endpoint returned non-JSON')) {
    // endpoint served HTML/text: guide user to manual account entry
  } else { throw error; }
}

Prevention

When it happens

Trigger: Calling the account endpoint via createProviderAccountWebContentFetchHandler, the HTTP request succeeds (result.ok), but responseLooksJson() fails: contentType lacks 'json' and the body doesn't parse/sniff as JSON (e.g. text/html login page, plain text, empty body).

Common situations: Endpoint URL misconfigured to a human-facing page; a captive portal or reverse proxy serving an HTML error/consent page; the provider's account URL requires session cookies the fetch doesn't carry; server returns XML or plain text instead of JSON.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/9bdccb9ffe5f3656. Report an issue: GitHub.