{"record":{"id":"530e93a9d7fcaee1","repo":"n8n-io/n8n","slug":"langsmithtelemetry-creates-its-own-tracer-do-not","errorCode":null,"errorMessage":"LangSmithTelemetry creates its own tracer — do not use .otlpEndpoint().","messagePattern":"LangSmithTelemetry creates its own tracer — do not use \\.otlpEndpoint\\(\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/@n8n/agents/src/integrations/langsmith.ts","lineNumber":372,"sourceCode":" *\n * const agent = new Agent('assistant')\n *   .model('anthropic/claude-sonnet-4-5')\n *   .telemetry(telemetry)\n *   .instructions('...');\n * ```\n */\nexport class LangSmithTelemetry extends Telemetry {\n\tprivate langsmithConfig?: LangSmithTelemetryConfig;\n\n\tconstructor(config?: LangSmithTelemetryConfig) {\n\t\tsuper();\n\t\tthis.langsmithConfig = config;\n\t}\n\n\t/** @override Build telemetry config, creating the LangSmith tracer. */\n\toverride async build(): Promise<BuiltTelemetry> {\n\t\tif (this.otlpEndpointValue !== undefined) {\n\t\t\tthrow new Error('LangSmithTelemetry creates its own tracer — do not use .otlpEndpoint().');\n\t\t}\n\n\t\t// Clear any tracer from a previous build() so the parent's\n\t\t// .tracer()/.otlpEndpoint() mutual-exclusion check passes cleanly.\n\t\tthis.tracerValue = undefined;\n\n\t\t// The LangSmith exporter silently drops all spans unless this is set.\n\t\t// Auto-enable it so users don't have to remember a magic env var.\n\t\tprocess.env.LANGCHAIN_TRACING_V2 ??= 'true';\n\n\t\tconst { tracer, provider } = await createLangSmithTracer(\n\t\t\tthis.langsmithConfig,\n\t\t\tthis.resolvedKey,\n\t\t);\n\t\tthis.tracerValue = tracer;\n\n\t\t// Call parent build() which handles integrations, redaction, etc.\n\t\tconst built = await super.build();","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/integrations/langsmith.ts#L354-L390","documentation":"Thrown by the HTTP Request validator when a sensitive header (Authorization, X-API-Key, X-Auth-Token, X-Access-Token, API-Key, APIKey — matched case-insensitively) is set to a literal value rather than an n8n expression or credential. The validator wants secrets to live in the credential system so they never get serialized into the workflow JSON. A hardcoded secret here is a leak risk and also blocks proper credential reuse.","triggerScenarios":"A node of type n8n-nodes-base.httpRequest whose parameters.headerParameters.parameters[] contains an entry where header.name lowercases to one of the sensitive set, header.value is truthy, and the value string neither starts with '=' nor contains '={{'. The exact gate is isSensitiveHeader(name) && value && !containsExpression(value).","commonSituations":"An LLM/AI builder pastes a curl-derived 'Authorization: Bearer <real token>' straight into headerParameters; copying vendor docs that show the API key inline; testing with a real key and forgetting to swap it for a credential reference.","solutions":["Move the secret into a credential: create an httpHeaderAuth or httpBearerAuth credential and reference it from the node instead of the header parameter.","If the provider uses 'Authorization: Bearer <token>', create a new credential with genericAuthType='httpTemplatedCustomAuth' and template {\"headers\":{\"Authorization\":\"Bearer {{api_key}}\"}}.","If you must keep the header inline for a throwaway test, wrap the value in an expression so it is not treated as a hardcoded literal (e.g. value: expr('<token>')) — but prefer a credential for anything persisted.","Reuse an existing httpHeaderAuth/httpBearerAuth credential id instead of creating a new one."],"exampleFix":"// before\nhttpRequest({\n  name: 'Get Data',\n  headerParameters: { parameters: [{ name: 'Authorization', value: 'Bearer abc123secret' }] },\n});\n\n// after — reuse a header auth credential\nhttpRequest({\n  name: 'Get Data',\n  authentication: 'genericCredentialType',\n  genericAuthType: 'httpHeaderAuth',\n  credentials: { httpHeaderAuth: { id: 'EXISTING_CRED_ID' } },\n});","handlingStrategy":"validation","validationCode":"import { isSensitiveHeader, containsExpression } from './validation-helpers';\n\nfunction findHardcodedSensitiveHeaders(headerParameters: { parameters?: Array<{ name?: string; value?: unknown }> } | undefined): string[] {\n  const offenders: string[] = [];\n  for (const h of headerParameters?.parameters ?? []) {\n    const valueStr = typeof h.value === 'string' ? h.value : JSON.stringify(h.value);\n    if (h.name && isSensitiveHeader(h.name) && h.value && !containsExpression(valueStr)) {\n      offenders.push(h.name);\n    }\n  }\n  return offenders;\n}\n\n// before building the node:\nconst bad = findHardcodedSensitiveHeaders(params.headerParameters);\nif (bad.length) throw new Error(`Move these headers into a credential: ${bad.join(', ')}`);","typeGuard":"function isExpressionValue(value: unknown): boolean {\n  return typeof value === 'string' && (value.startsWith('=') || value.includes('={{'));\n}","tryCatchPattern":null,"preventionTips":["Never type a real secret into headerParameters; always reference a credential.","Run validate_workflow in CI and fail on any HARDCODED_CREDENTIALS issue.","Keep a small allowlist of test-only header values that are clearly fake."],"tags":["security","credentials","http-request","ai-generated"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}