{"record":{"id":"90e55afe4ca8fae0","repo":"JuliusBrussee/caveman","slug":"cavemanexporterconfig-protocol-protocol-is-not","errorCode":null,"errorMessage":"cavemanExporterConfig: protocol ${protocol} is not accepted by ${baseUrl}/otlp/v1/traces; the Caveman OTLP endpoint decodes protobuf-JSON only. Use 'http/json'.","messagePattern":"cavemanExporterConfig: protocol (.+?) is not accepted by (.+?)/otlp/v1/traces; the Caveman OTLP endpoint decodes protobuf-JSON only\\. Use 'http/json'\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/mastra/src/index.ts","lineNumber":93,"sourceCode":" * Builds the OTLP settings a Mastra application passes to its telemetry config.\n *\n * The endpoint is Caveman's OTLP route (`POST {baseUrl}/otlp/v1/traces`). That\n * handler decodes protobuf-JSON, not binary protobuf, so `http/json` is the only\n * protocol it can accept — a protobuf or gRPC batch is rejected rather than\n * silently dropped here.\n *\n * Auth: the gateway reads `x-cave-api-key` first and falls back to\n * `authorization` (a `Bearer ` prefix is stripped before the key is parsed), so\n * both headers are emitted and either one alone would authenticate.\n */\nexport function cavemanExporterConfig(options: CavemanExporterOptions): CavemanExporterConfig {\n  const baseUrl = requireBaseUrl(options.baseUrl, \"cavemanExporterConfig: baseUrl\");\n  const apiKey = requireNonEmpty(options.apiKey, \"cavemanExporterConfig: apiKey\");\n  const projectId = requireNonEmpty(options.projectId, \"cavemanExporterConfig: projectId\");\n  const environment = requireNonEmpty(options.environment, \"cavemanExporterConfig: environment\");\n  const protocol = options.protocol ?? \"http/json\";\n  if (protocol !== \"http/json\") {\n    throw new Error(\n      `cavemanExporterConfig: protocol ${protocol} is not accepted by ${baseUrl}/otlp/v1/traces; ` +\n        \"the Caveman OTLP endpoint decodes protobuf-JSON only. Use 'http/json'.\",\n    );\n  }\n\n  const endpoint = `${baseUrl}/otlp/v1/traces`;\n  const headers: Record<string, string> = {\n    \"x-cave-api-key\": apiKey,\n    authorization: `Bearer ${apiKey}`,\n  };\n  if (options.agentId) headers[\"x-cave-agent\"] = options.agentId;\n  Object.assign(headers, options.headers ?? {});\n\n  // §23.3 of docs/self_learning_implementation_spec.md — the required metadata block.\n  const resourceAttributes: Record<string, string> = {\n    \"caveman.project.id\": projectId,\n    \"caveman.environment\": environment,\n  };","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/mastra/src/index.ts#L75-L111","documentation":"cavemanExporterConfig builds an OTLP trace exporter for the Caveman gateway, which only accepts OTLP protobuf-JSON over HTTP at {baseUrl}/otlp/v1/traces. The protocol option exists for API symmetry, but any value other than the default 'http/json' (e.g. 'grpc' or 'http/protobuf') is rejected at config time with an explanatory message, because the gateway cannot decode binary protobuf bodies.","triggerScenarios":"Calling cavemanExporterConfig({ ..., protocol: 'grpc' }) or copying an OpenTelemetry SDK NodeSDK config where the protocol field was set for a standard OTLP exporter.","commonSituations":"Porting code from @opentelemetry/exporter-trace-otlp-grpc or -http, boilerplate that parameterizes protocol per environment, or assuming symmetry with the OTel spec's http/protobuf option.","solutions":["Remove the protocol option (it defaults to http/json) or set it to 'http/json' explicitly.","If you need grpc/binary OTLP, export to a different collector endpoint and keep Caveman on http/json.","Update the TypeScript type usage: CavemanExporterOptions['protocol'] only permits the JSON value."],"exampleFix":"// before\ncavemanExporterConfig({ baseUrl, apiKey, projectId, environment, protocol: \"grpc\" });\n// after\ncavemanExporterConfig({ baseUrl, apiKey, projectId, environment });","handlingStrategy":"type-guard","validationCode":"const PROTOCOL = \"http/json\" as const;\n// Only pass protocol when it equals the accepted value\nconst cfg = cavemanExporterConfig({ baseUrl, apiKey, projectId, environment, ...(protocol === PROTOCOL ? { protocol } : {}) });","typeGuard":"type AcceptedProtocol = \"http/json\";\nfunction isAcceptedProtocol(v: string): v is AcceptedProtocol {\n  return v === \"http/json\";\n}","tryCatchPattern":null,"preventionTips":["Treat http/json as the only protocol when targeting the Caveman OTLP endpoint.","Do not copy OTel SDK protocol settings into cavemanExporterConfig.","Let the default apply — omit protocol unless you need to be explicit."],"tags":["telemetry","otlp","validation","config"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}