n8n-io/n8n · error · Error

OpenTelemetry active span callback is required.

Error message

OpenTelemetry active span callback is required.

What it means

Thrown by MetadataEnrichedTracer.startActiveSpan when the call resolves optionsOrFn to a SpanOptions object but neither contextOrFn nor fn is a function — i.e. the caller invoked the three-or-four-arg overload without supplying the required active-span callback. The OpenTelemetry Tracer contract requires the callback so the span can be activated around its execution.

Source

Thrown at packages/@n8n/agents/src/sdk/metadata-enriched-tracer.ts:52

	startActiveSpan<F extends (span: Span) => unknown>(
		name: string,
		optionsOrFn: SpanOptions | F,
		contextOrFn?: Context | F,
		fn?: F,
	): ReturnType<F> {
		if (typeof optionsOrFn === 'function') {
			return this.delegate.startActiveSpan(name, optionsOrFn);
		}

		const options = {
			...optionsOrFn,
			attributes: { ...this.attributes, ...optionsOrFn.attributes },
		};
		if (typeof contextOrFn === 'function') {
			return this.delegate.startActiveSpan(name, options, contextOrFn);
		}
		if (contextOrFn === undefined || fn === undefined) {
			throw new Error('OpenTelemetry active span callback is required.');
		}

		return this.delegate.startActiveSpan(name, options, contextOrFn, fn);
	}
}

export function createMetadataEnrichedTracer(
	tracer: Tracer,
	metadata: Record<string, AttributeValue> | undefined,
): Tracer {
	if (!metadata || Object.keys(metadata).length === 0) return tracer;

	const attributes = Object.fromEntries(
		Object.entries(metadata).map(([key, value]) => [`ai.telemetry.metadata.${key}`, value]),
	);
	return new MetadataEnrichedTracer(tracer, attributes);
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Always pass the callback as the final argument: startActiveSpan(name, opts, fn) or startActiveSpan(name, opts, context, fn).
  2. When forwarding variadic args, normalize to one of the documented overload shapes before delegating.
  3. Avoid as-casts on the Tracer; let TypeScript enforce the overload.

Example fix

// before
(tracer as any).startActiveSpan('span', { attributes });
// after
tracer.startActiveSpan('span', { attributes }, (span) => {
  try { /* work */ } finally { span.end(); }
});
Defensive patterns

Strategy: validation

Validate before calling

function assertActiveSpanCallback(args: unknown[]) {
  const last = args[args.length - 1];
  if (typeof last !== 'function') {
    throw new TypeError('startActiveSpan requires a callback as the final argument');
  }
}

Type guard

function hasSpanCallback(args: unknown[]): boolean {
  return typeof args[args.length - 1] === 'function';
}

Prevention

When it happens

Trigger: Only reachable when startActiveSpan is called with (name, SpanOptions, Context | undefined, undefined) — i.e. the callback argument is missing while earlier args are objects. Through typed TypeScript the overload signatures forbid this; it surfaces from JS callers, any/unknown-typed call sites, or as-casts that defeat the overloads.

Common situations: Third-party telemetry adapters forwarding variadic args into startActiveSpan; reflection-based tracer wrappers that drop the callback; misuse via cast (tracer as any).startActiveSpan(name, opts, ctx).

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/a5ba71a34e3be156. Report an issue: GitHub.