openzipkin/zipkin · error · IllegalStateException
Missing :
Error message
Missing :
What it means
Span.Builder.build() throws IllegalStateException('Missing : traceId') / ('Missing : id') when the span is built without the two mandatory fields. Unlike bad-format errors thrown at setter time, this is a completeness check at build time: traceId and id must both have been set (via any overload). The message concatenates every missing field, e.g. 'Missing : traceId id'.
Source
Thrown at zipkin/src/main/java/zipkin2/Span.java:590
flags |= FLAG_SHARED;
} else {
flags &= ~FLAG_SHARED;
}
return this;
}
/** Sets {@link Span#shared} */
public Builder shared(@Nullable Boolean shared) {
if (shared != null) return shared((boolean) shared);
flags &= ~(FLAG_SHARED_SET | FLAG_SHARED);
return this;
}
public Span build() {
String missing = "";
if (traceId == null) missing += " traceId";
if (id == null) missing += " id";
if (!missing.isEmpty()) throw new IllegalStateException("Missing :" + missing);
if (id.equals(parentId)) { // edge case, so don't require a logger field
Logger logger = Logger.getLogger(Span.class.getName());
if (logger.isLoggable(FINEST)) {
logger.fine(format("undoing circular dependency: traceId=%s, spanId=%s", traceId, id));
}
parentId = null;
}
// shared is for the server side, unset it if accidentally set on the client side
if ((flags & FLAG_SHARED) == FLAG_SHARED && kind == Kind.CLIENT) {
Logger logger = Logger.getLogger(Span.class.getName());
if (logger.isLoggable(FINEST)) {
logger.fine(format("removing shared flag on client: traceId=%s, spanId=%s", traceId, id));
}
shared(null);
}
return new Span(this);
}
View on GitHub (pinned to 878ce2a1fa)
Solutions
- Ensure every code path sets both .traceId(...) and .id(...) before build(); check the conditional branches that skip them.
- If a trace context may be absent, branch explicitly: either start a new trace (generate both IDs) or skip reporting — never build a partial span.
- Move build() after all setters in the method so no early return path can bypass ID assignment.
Example fix
// before Span.Builder b = Span.newBuilder().id(spanId); if (traceContext != null) b.traceId(traceContext.traceIdString()); return b.build(); // ISE when traceContext == null // after Span.Builder b = Span.newBuilder().id(spanId); if (traceContext == null) return null; // or generate a new trace id return b.traceId(traceContext.traceIdString()).build();
Defensive patterns
Strategy: validation
Validate before calling
// before build(): ensure mandatory fields
if (traceId == null || spanId == null) {
throw new IllegalStateException("refusing to build incomplete span");
} Try / catch
try {
return builder.build();
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Missing :")) { /* log + drop malformed span */ }
else throw e;
} Prevention
- Keep all mandatory setters on one unconditional code path before build().
- In collectors, catch 'Missing :' ISE to quarantine malformed inbound spans instead of crashing the consumer.
When it happens
Trigger: Building a span after setting only id (trace ID never set), only traceId, or neither — often when the code builds first and conditionally sets IDs afterwards, or when ID-setting code is skipped on an error path that still reaches build().
Common situations: Shared span-building helper where some callers pass a nullable trace context and the helper skips the setter on null; async/reporter code that rebuilds partial spans for error reporting; refactoring that moved traceId() out of a base method.
Related errors
AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14).
Data as JSON: /api/errors/af4d32e2243d1b5b.
Report an issue: GitHub.