{"record":{"id":"e059a4df597ba1fe","repo":"musistudio/claude-code-router","slug":"unsupported-provider-protocol-value","errorCode":null,"errorMessage":"Unsupported provider protocol: ${value}","messagePattern":"Unsupported provider protocol: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/contracts/deep-link.ts","lineNumber":497,"sourceCode":"  const url = new URL(value);\n  if (url.protocol !== \"https:\") {\n    throw new Error(\"Provider manifest URL must use https.\");\n  }\n  if (url.username || url.password) {\n    throw new Error(\"Provider manifest URL cannot include credentials.\");\n  }\n  if (!url.hostname) {\n    throw new Error(\"Provider manifest URL is invalid.\");\n  }\n}\n\nfunction normalizeProviderProtocol(value: string | undefined): GatewayProviderProtocol | undefined {\n  if (!value) {\n    return undefined;\n  }\n  const protocol = value.trim();\n  if (!providerProtocols.has(protocol as GatewayProviderProtocol)) {\n    throw new Error(`Unsupported provider protocol: ${value}`);\n  }\n  return protocol as GatewayProviderProtocol;\n}\n\nfunction readDeepLinkModels(params: URLSearchParams, payload: Record<string, unknown> | undefined): string[] {\n  const values = [\n    ...params.getAll(\"models\"),\n    ...payloadModels(payload)\n  ];\n  const seen = new Set<string>();\n  const models: string[] = [];\n\n  for (const value of values) {\n    for (const model of splitModelValue(value)) {\n      if (model.length > maxModelLength) {\n        throw new Error(\"Model name is too long.\");\n      }\n      if (seen.has(model)) {","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/contracts/deep-link.ts#L479-L515","documentation":"normalizeProviderProtocol validates an optional protocol field on provider deep links/manifests against the known GatewayProviderProtocol set (providerProtocols). An unrecognized protocol string (e.g. 'openai-completions' when only specific values are supported) throws with the offending value embedded.","triggerScenarios":"Passing protocol=\"anthropic-beta\" or any string not in providerProtocols in a ccr:// link or provider manifest.","commonSituations":"Providers adding new protocol identifiers before this library supports them; typos/casing differences ('OpenAI' vs 'openai'); manifests written against a different gateway's schema.","solutions":["Use only the protocol identifiers exported/supported by the current version (check the GatewayProviderProtocol type / providerProtocols set)","Omit protocol to use the default rather than guessing a value","Upgrade the library if a newly added provider protocol should be supported"],"exampleFix":"// before\n{\"provider\":{\"protocol\":\"openai-chat-completions\",\"base_url\":\"...\"}}\n// after\n{\"provider\":{\"protocol\":\"openai\",\"base_url\":\"...\"}}","handlingStrategy":"type-guard","validationCode":"const known = new Set([\"openai\", \"anthropic\", ...]); if (protocol && !known.has(protocol)) return useDefault();","typeGuard":"const isSupportedProtocol = (v: string | undefined, known: ReadonlySet<string>): v is string => !v || known.has(v.trim());","tryCatchPattern":"try { parseProviderDeepLinkPayload(url); } catch (e) { if (e instanceof Error && e.message.startsWith(\"Unsupported provider protocol\")) return fallbackToDefaultProtocol(); throw e; }","preventionTips":["Reference the exported GatewayProviderProtocol values, not string literals","Omit protocol rather than guessing; keep library updated for new protocol ids"],"tags":["protocol","validation","deep-link","manifest"],"backgroundTag":"unsupported-enum-value","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}