Cloudflare Workers Custom Spans Just Got Exception Recording and Method Chaining!
Hi, it's me! I just found some exciting news about Workers tracing, and I couldn't wait to tell you about it!
Cloudflare ChangelogWhat was announced?
According to the Cloudflare Changelog, on September 25, 2026, Cloudflare added four new APIs to the custom spans feature in Workers: tracing.startSpan(), tracing.getActiveSpan(), span.recordException(), and span.setAttributes(). This update expands support for more of the OpenTelemetry span API, so you can instrument more of your code and record errors directly on your spans.
The story so far
Workers already had a custom spans feature that let you create your own trace units. But based on this announcement, it looks like there wasn't a built-in way to record exceptions directly on a span, set multiple attributes at once, or quickly grab the currently active span from a helper function. setAttribute() also didn't return the span, so chaining wasn't possible before.
What changes
Here's what gets better with this update:
- You can call
span.recordException(err)right in your catch block to record the exception on the span, making it easier to debug what went wrong tracing.getActiveSpan()lets you annotate the current span from helper functions and libraries, without threading the span object through your codesetAttribute()andsetAttributes()now return the span, so you can chain callstracing.startSpan(name)lets you create a standalone span that isn't set as the active span
This is a quiet but genuinely useful update if you're building out observability on Workers.
Dive Deep
Let's start with tracing.startSpan(name). It creates a span, but calling it alone doesn't make that span active, so other spans won't nest under it. When the operation is done, call span.end() to close it out.
Next up is tracing.getActiveSpan(). This returns whichever span is currently active. You can call it from a helper function or library to add attributes to the span in progress, without passing the span object around. If you call it outside of any custom span, it returns the invocation's root span.
Then there's span.recordException(exception). This records an exception event on a span, and it accepts an Error object, a string, or an object with code, name, or message properties. Call it right after you catch an error, and the details land straight in your trace.
Finally, span.setAttributes(attributes) lets you set multiple attributes at once. Both setAttribute() and setAttributes() now return the span, so you can chain calls.
Here's the sample from Cloudflare's own docs:
import { tracing } from "cloudflare:workers";
export default {
async fetch(request, env) {
const user = await authenticate(request, env);
// Annotate the invocation's root span
tracing.getActiveSpan()?.setAttributes({
"user.id": user.id,
"user.plan": user.plan,
});
const span = tracing.startSpan("load-profile");
try {
return Response.json(await loadProfile(env, user.id));
} catch (err) {
span.recordException(err);
throw err;
} finally {
span.end();
}
},
};
In this example, after authenticate() resolves the user, tracing.getActiveSpan()?.setAttributes() tags the request's root span with user.id and user.plan. Then tracing.startSpan("load-profile") creates a separate span to time the loadProfile() call. If something throws, span.recordException(err) records it before re-throwing, and the finally block always calls span.end() to close the span.
Wrap-up
tracing.startSpan(name)creates a standalone span; callspan.end()when you're done with ittracing.getActiveSpan()lets you grab the active span from helper functions and librariesspan.recordException(exception)records anError, a string, or an object withcode/name/messageas an exception eventspan.setAttributes(attributes)sets multiple attributes at once, and bothsetAttribute()/setAttributes()now support chaining- If you're building or planning OpenTelemetry-based observability on Workers, this one's worth trying right away!