{"record":{"id":"ec60cdd048323052","repo":"n8n-io/n8n","slug":"cannot-set-both-tracer-and-otlpendpoint-us","errorCode":null,"errorMessage":"Cannot set both .tracer() and .otlpEndpoint() — use one or the other.","messagePattern":"Cannot set both \\.tracer\\(\\) and \\.otlpEndpoint\\(\\) — use one or the other\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/sdk/telemetry.ts","lineNumber":329,"sourceCode":"\t\treturn this;\n\t}\n\n\t/**\n\t * Set an OTLP endpoint to auto-create a tracer + provider.\n\t * Requires `@opentelemetry/sdk-trace-node`, `@opentelemetry/exporter-trace-otlp-http`,\n\t * and `@opentelemetry/sdk-trace-base` as peer dependencies.\n\t *\n\t * Mutually exclusive with `.tracer()`.\n\t */\n\totlpEndpoint(value: string): this {\n\t\tthis.otlpEndpointValue = value;\n\t\treturn this;\n\t}\n\n\t/** Build the telemetry configuration. */\n\tasync build(): Promise<BuiltTelemetry> {\n\t\tif (this.tracerValue !== undefined && this.otlpEndpointValue !== undefined) {\n\t\t\tthrow new Error('Cannot set both .tracer() and .otlpEndpoint() — use one or the other.');\n\t\t}\n\n\t\tlet tracer: OpaqueTracer = this.tracerValue;\n\t\tlet provider: OpaqueTracerProvider | undefined;\n\n\t\tif (this.otlpEndpointValue !== undefined) {\n\t\t\tconst otlp = await createOtlpTracer(this.otlpEndpointValue);\n\t\t\ttracer = otlp.tracer;\n\t\t\tprovider = otlp.provider;\n\t\t}\n\n\t\tconst redactFn = this.redactFn;\n\t\tconst customIntegrations = redactFn\n\t\t\t? this.integrationsList.map((integration) =>\n\t\t\t\t\twrapIntegrationWithRedaction(integration, redactFn),\n\t\t\t\t)\n\t\t\t: [...this.integrationsList];\n\t\tlet resolveIntegrations: BuiltTelemetry['resolveIntegrations'];","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/sdk/telemetry.ts#L311-L347","documentation":"The Telemetry builder offers two mutually exclusive ways to source a tracer: a pre-built tracer via .tracer() or an OTLP HTTP endpoint URL via .otlpEndpoint() (which auto-constructs the tracer/provider). Setting both creates an ambiguity about which tracer the agent should use, so build() rejects the combination up front rather than silently preferring one.","triggerScenarios":"Calling both new Telemetry().tracer(myTracer) and .otlpEndpoint('http://...') on the same builder instance, then awaiting .build() or passing the builder to an Agent which triggers the build.","commonSituations":"Copy-pasting telemetry config from two examples; refactoring from .tracer() to .otlpEndpoint() (or vice versa) without removing the prior call; merging config from a base profile that sets one and an override that sets the other.","solutions":["Pick one source: if you have a pre-built tracer (e.g. from LangSmithTelemetry or a shared provider), keep .tracer() and remove .otlpEndpoint().","If you only have an OTLP URL, keep .otlpEndpoint() and remove the .tracer() call.","Audit the builder chain for both method calls and delete the one not matching your infrastructure.","If config is conditional, guard with an if/else so only one of the two methods runs per build."],"exampleFix":"// before\nnew Telemetry()\n  .tracer(myTracer)\n  .otlpEndpoint('http://collector:4318') // throws on build\n\n// after — choose one\nnew Telemetry().tracer(myTracer);\n// or\nnew Telemetry().otlpEndpoint('http://collector:4318');","handlingStrategy":"validation","validationCode":"const telemetry = new Telemetry();\nif (process.env.OTLP_ENDPOINT) {\n  telemetry.otlpEndpoint(process.env.OTLP_ENDPOINT);\n} else if (sharedTracer) {\n  telemetry.tracer(sharedTracer);\n}\n// never set both — this if/else guarantees mutual exclusion","typeGuard":"function hasBothTracerSources(b: { tracerValue?: unknown; otlpEndpointValue?: unknown }): boolean {\n  return b.tracerValue !== undefined && b.otlpEndpointValue !== undefined;\n}","tryCatchPattern":"try {\n  await telemetry.build();\n} catch (err) {\n  if (err instanceof Error && err.message.includes('.tracer() and .otlpEndpoint()')) {\n    // decide which to keep based on infra, clear the other, rebuild\n  } else throw err;\n}","preventionTips":["Drive tracer-source selection from a single config flag (e.g. presence of OTLP_ENDPOINT env var) so both calls can never fire.","Avoid chaining both methods conditionally without an else.","Review telemetry setup during code review specifically for this mutual-exclusion rule."],"tags":["telemetry","configuration","opentelemetry","mutually-exclusive"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}