Getting TraceID from an OpenTracing DatadogSpanContext can throw "Cannot read properties of undefined (reading 'toString') #4987
Description
Calling this DatadogSpanContext.toTraceId()
method on what appears to be a valid span can sometimes error out with an undefined inner this._traceId
value:
dd-trace-js/packages/dd-trace/src/opentracing/span_context.js
Lines 33 to 40 in 497c0b0
Suggested Solution
-
While I am not sure what might cause this scenario, a quick fix would be to make the dot property access of
this._traceId
a bit more defensive:toTraceId (get128bitId = false) { if (this._traceId && get128bitId) { return this._traceId.toBuffer().length <= 8 && this._trace.tags[TRACE_ID_128] ? this._trace.tags[TRACE_ID_128] + this._traceId.toString(16).padStart(16, '0') : this._traceId.toString(16).padStart(32, '0') } return this._traceId?.toString(10) }
Replication Steps
-
Using:
"dd-trace": "5.25.0",
-
Initialize the tracer on startup:
import tracer from 'dd-trace'; import { version } from '../../package.json'; import { buildVars } from '#root/.build/buildVars'; tracer.init({ service: process.env.DD_SERVICE || 'my-fe-app', version: version ? `${version}-${buildVars.GIT_SHA}` : '0.0.0', tags: { pid: process.pid, }, runtimeMetrics: true, logInjection: true, });
-
Try to defensively get the active span's
traceID
at runtimeimport tracer from 'dd-trace'; ... const span = tracer.scope().active(); const traceID = span?.context()?.toTraceId() ?? 'n/a'; ...
Expected result
traceID
is either a valid Trace ID from the current span orundefined
in which case we default to'n/a/
based on the code above
Actual result
-
We regularly encounter an error whereby we seem to have a defined
span
(i.e. notnull
orundefined
) with an empty or missingDatadogSpanContext._traceID
property such that when we calltoTraceId()
on it we get the following error:.../my_app/node_modules/dd-trace/packages/dd-trace/src/opentracing/span_context.js:39 return this._traceId.toString(10) ^ TypeError: Cannot read properties of undefined (reading 'toString') at DatadogSpanContext.toTraceId (.../my_app/packages/dd-trace/src/opentracing/span_context.js:39:26)
Unit Tests
-
Here is a failing test we can add to
packages/dd-trace/test/opentracing/span_context.spec.js
to demonstrate the issue'use strict' require('../setup/tap') const { expect } = require('chai') const id = require('../../src/id') describe('SpanContext', () => { let SpanContext let TraceState beforeEach(() => { SpanContext = require('../../src/opentracing/span_context') TraceState = require('../../src/opentracing/propagation/tracestate') }) ... describe('toTraceparent()', () => { ... it('should allow nullsafe access to trace ID', () => { const spanContext = new SpanContext({ traceId: undefined, spanId: id('456', 16) }) spanContext._trace.tags['_dd.p.tid'] = null expect(spanContext.toTraceId()).to.equal(undefined) }) }) })