pinpoint-apm/pinpoint · error · OtlpTraceParseException
OtlpTraceParseException (syntax error, message built from sy
Error message
OtlpTraceParseException (syntax error, message built from syntaxErrorMessage(e))
What it means
OtlpJsonTraceParser.parse() parses the OTLP/HTTP JSON body into an ExportTraceServiceRequest protobuf via Jackson's ProtoJson parser. A JsonProcessingException (malformed JSON, wrong types, invalid field names) is wrapped as OtlpTraceParseException with a syntax-error message. The request is rejected because it is not valid OTLP JSON.
Source
Thrown at otlptrace/otlptrace-collector/src/main/java/com/navercorp/pinpoint/otlp/trace/collector/controller/OtlpJsonTraceParser.java:75
private static final Set<String> ID_FIELD_NAMES = Set.of(
"traceId", "trace_id",
"spanId", "span_id",
"parentSpanId", "parent_span_id");
private static final JsonFactory JSON_FACTORY = new JsonFactory();
private static final JsonFormat.Parser PROTO_JSON_PARSER = JsonFormat.parser().ignoringUnknownFields();
private OtlpJsonTraceParser() {
}
public static ExportTraceServiceRequest parse(byte[] body) {
try {
final String protoJson = rewriteIdsToBase64(body);
final ExportTraceServiceRequest.Builder builder = ExportTraceServiceRequest.newBuilder();
PROTO_JSON_PARSER.merge(protoJson, builder);
return builder.build();
} catch (JsonProcessingException e) {
throw new OtlpTraceParseException(syntaxErrorMessage(e), e);
} catch (IOException | IllegalArgumentException e) {
throw new OtlpTraceParseException(e.getMessage(), e);
}
}
/**
* Jackson's {@link JsonProcessingException#getMessage()} appends the full {@code JsonLocation}
* ({@code [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1,
* column: 19]}), sometimes twice, which more than doubles the message without adding anything a
* client can act on. Keep the cause and the position only — in the shape the OTel collector's
* own JSON errors take.
*/
static String syntaxErrorMessage(JsonProcessingException e) {
// The original message can embed a location too ("(for Array starting at [Source: ...])").
final String original = SOURCE_LOCATION.matcher(e.getOriginalMessage()).replaceAll("line $1, column $2");
final JsonLocation location = e.getLocation();
if (location == null) {
return original;View on GitHub (pinned to 744c3d3075)
Solutions
- Validate the request body with a strict JSON parser and a proto JSON schema check before sending; generate the body with ExportTraceServiceRequest JSON serialization rather than hand-built JSON.
- Confirm the client targets the JSON endpoint with Content-Type: application/json (binary protobuf goes to the protobuf endpoint).
- Use the error's syntax message (line/column info from Jackson) to locate the malformed portion of the payload.
- Ensure all trace/span IDs are base64-encoded when using the JSON format (the server rewrites ids to base64 before parsing).
Example fix
// before
String json = "{\"resourceSpans\": [" + partial + "}"; // hand-built, truncated
// after
String json = JsonFormat.printer().omittingInsignificantWhitespace().print(exportRequest); Defensive patterns
Strategy: validation
Validate before calling
ObjectMapper om = new ObjectMapper();
try (JsonParser p = om.createParser(body)) {
while (p.nextToken() != null) {} // syntax check before sending
} Type guard
static boolean looksLikeOtlpJson(String body) {
return body != null && body.trim().startsWith("{") && body.contains("resourceSpans");
} Try / catch
try {
byte[] resp = sendOtlpJson(body);
} catch (OtlpTraceParseException e) {
log.error("OTLP JSON rejected: {}", e.getMessage());
// fix payload or switch to protobuf binary endpoint
} Prevention
- Serialize OTLP requests with the official protobuf JSON printer, never hand-built JSON
- Send JSON only to the JSON endpoint with Content-Type: application/json
- Base64-encode all bytes/ID fields as required by proto3 JSON mapping
- Validate payloads against the opentelemetry-proto schema in CI
When it happens
Trigger: POSTing a trace export request to the OTLP JSON trace collector endpoint whose body fails JSON parsing: malformed JSON syntax, unknown fields, wrong value types (e.g. string where bytes expected), or IDs encoded other than base64.
Common situations: SDKs or scripts hand-rolling OTLP JSON instead of using protobuf JSON serialization; proxies mangling the body; base64/protobuf binary body sent to the JSON endpoint; truncated request bodies.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- OtlpTraceParseException (message from e.getMessage())
- OTLP/HTTP decompressed request body exceeded max size: limit
- OTLP/HTTP request body exceeded max size: limit=${limit}
- Resource attribute `service.name` is required to save OTLP m
- Resource attribute `pinpoint.agentId` is required to save OT
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/0c2ddf3c829168da.
Report an issue: GitHub.