Reporting Exceptions to Honeycomb with Frontend Observability
There are at least two ways to report error details in OpenTelemetry. Web applications generally place exceptions in trace spans as span events, and mobile applications send exceptions as log messages instead. This article will help you understand which approaches you can use, and how the errors will appear in Honeycomb.

By: Ken Rimple

It’s 2025, The Frontend Deserves Observability Too
Watch Now
So you've built a client application and you've started sending telemetry. The information sent back by this client is vital to you, and one of the first things you care about is capturing and reporting errors.
There are at least two ways to report error details in OpenTelemetry. Web applications generally place exceptions in trace spans as span events, and mobile applications send exceptions as log messages instead. This article will help you understand which approaches you can use, and how the errors will appear in Honeycomb.
RUM can leave questions unanswered. Honeycomb for Frontend Observability doesn’t.
Honeycomb and exception visualizations
The platform reports errors by marking the span in error (red coloring and captured in the ‘Spans with Errors’ locator). It places a red circle around the exception event that marks the details:

In all cases, you should mark the span that received the error to a spanStatus of ERROR. The important piece is the code, which sets the error attribute to true.
// JavaScript Web example
span.setStatus(SpanStatusCode.ERROR);
// iOS example
span.status = .ok
// Android example:
span.setStatus(StatusCode.ERROR)Providing a message in an error span
You may have noticed that you can add a message to your error spans when setting the span status. This does not add exception details. Instead, it adds a status_message to the span. According to the OpenTelemetry documentation, you should set the message to the exception message, if present:
// JavaScript Web example
span.setStatus({
code: SpanStatusCode.ERROR,
message: exception.getMessage()
);
// iOS example
span.status = .error(description: error.localizedDescription)
// Android example:
span.setStatus(StatusCode.ERROR, exception.getMessage())The examples below all provide the status message.
Armed with the ability to set the span status and message, let's look at how to provide the exception details.
Attaching an exception to a trace span
Both the core OpenTelemetry Frontend SDKs and all Honeycomb Frontend SDKs support two mechanisms to attach an application exception to a span:
- Using
recordExceptionon a span will add a span event to the span itself. Most OpenTelemetry APIs have a variant of this method and it is used in a lot of web frontend code. - Using a log record to record the exception and attach it to the span with the
trace.trace_idandtrace.parent_id attributes.
Let's look at each technique.
Recording an exception in a span event
Each trace span includes a collection of span events, which are visible in Honeycomb as colored circles marked at the time their event occurred.

Span events for errors should be named exception. When attached to a span with an error status, they will be colored red, as shown in the third span above.
A web frontend records the exception into a span event using recordException. To do this, you must:
- Mark the span in error, and add the message to it (optionally)
- Use
span.recordExceptionto add the span event to the span containing the exception. It will automatically have the nameexception
// get the current span (alternatively, create one and end it later)
const activeSpan = trace.getSpan(context.active());
try {
// something
} catch (e) {
activeSpan.setStatus({
status: SpanStatusCode.ERROR,
message: e.getMessage()
);
activeSpan.recordException(e);
}This example assumes we have accessed or created an active span, and that we're just adding the exception to it, not ending it here.
This is not the only approach, but there is a lot of existing code and auto-instrumentation that uses this mechanism. The main issue with span events is that they are only sent once the span is ended and queued for transmission. This is why the second mechanism exists: recording the error as a log message.
Recording an exception with a trace-participating log message
In mobile telemetry, the preferred mechanism for capturing device crashes and exceptions is by attaching a log record to the trace and span. The log message can be sent right away, even if the span is never sent (due to forgetting to end the span or a crashing frontend).
Since mobile SDKs also all center their debugging around a logging mechanism, and logs are the currency of error messages in many systems, this approach is recommended.
With Android (using the Android OTel SDK):
- Mark the span in error and add the message to it (optionally)
- Grab the
loggerProviderfrom the SDK instance - Create a
Loggerinstance - Create a log message with key attributes including a stack trace, exception type, and the
error.message,error.stacktrace, anderror.typeproperties.
With iOS:
- Mark the span in error and add the message to it (optionally)
- Access the
defaultErrorLoggerfromOpenTelemetryApi.Logger - Use it to build a log record
- Emit the log record including a stack trace, exception type, and the same error properties as above
In general, you should probably create your own wrapper to define these values consistently, but Honeycomb's SDKs come to the rescue!
Using the Honeycomb logging helper methods
Honeycomb provides enablement SDKs for web and mobile-native applications to make onboarding easier. These SDKs send device-specific attributes in spans, enable device-specific telemetry, and other features. You’re using OpenTelemetry when you install these SDKs.
Honeycomb's mobile frontend SDKs provide helper methods to log your exceptions: Honeycomb.log in the Honeycomb Swift SDK, and Honeycomb.logException in the Honeycomb Android SDK.
iOS:
do {
// something
} catch {
span.status = .error(description: error.localizedDescription)
Honeycomb.log(
error: error,
attributes: ["name" : .string("exception")],
thread: Thread.main
)
}Android:
try {
// something
} catch (exception) {
activeSpan.setStatus(StatusCode.ERROR, e.message)
rum?.let { // assigns rum to 'it' below
Honeycomb.logException(
it
exception,
Attributes.of(
AttributeKey.stringKey("name"),
"exception",
),
Thread.currentThread()
)
}
}The examples above log the errors properly as log signals, and attach them to their traces in OpenTelemetry. Wrapper methods like these let Honeycomb add specific exception details as individual OpenTelemetry attributes, which helps in decoding stack traces, categorizing errors, and tracking duplicate error conditions during investigations.
How Honeycomb shows errors
Honeycomb stack traces will display spans with the error status with red span bars.

Not all errors generate exception events. Examples of errors without these events include errors that are retried, or expected, or happen frequently in an API that records errors but doesn't result in a failure in the code.
For errors that have recorded exception events, Honeycomb will show the event with a red circle, regardless of whether the exception comes from a span event or log message:

Click on the circle and the exception attributes will appear on the attribute explorer on the right hand side of the trace view:

You can review the type of signal used to record the exception in the ‘Fields’ panel. Both approaches show the meta.span_kind as span_event, so don't look there. Instead, look at the meta.signal_type field.
Exception span events have a signal type of trace:

A trace-participating log exception is attached to a log signal. The signal type will be log:

This is how OpenTelemetry incorporates the log into the full trace: it adds the trace.trace_id attribute, which points to the trace itself, and the trace.parent_id attribute, which points to the trace span the log message is attached to:

Which approach should I use?
Why use span events?
Span events are sent with the rest of the span. They don't require you to set up log ingest at the frontend tier, either. For web applications this can be an advantage. Span events are queryable separately, and cost the same as a separate log event.
Why trace-participating log messages?
As we discussed earlier, logs have several advantages over traces for frontend observability. They are sent immediately to the logging system, rather than waiting for a trace span to end, and can be sent even if your application crashes before a longer running span completes.
Armed with these concepts, you can confidently set your span statuses, provide span error status messages, and report errors either as span events or log messages.
Choose the method that best suits you: if you have configured your logging telemetry pipelines and are sending log messages, you can take advantage of trace-participating logs and get the messages immediately without fear of losing them due to crashes or improperly ended spans. If your existing web application heavily uses recordException, you can use span events.
If you have any further questions or need me to expand on some of these concepts, feel free to book time with me! I’d love to hear from you.