fix(transform): don't panic on absent optional OTLP fields - #17
Conversation
ResourceFromPB, LogValueFromPB and attrValue each dereferenced a pointer
that the OTLP proto marks optional, so a valid payload that simply omits
the field crashed the conversion with a nil pointer dereference.
resource is optional on ResourceSpans, ResourceLogs and ResourceMetrics
("if this field is not set then no resource info is known"), reaching
users through SpansFromPB, ReexportLogsFromPB and ResourceMetricsFromPB.
body is optional on LogRecord, so an attribute-only record — one that
carries its data in attributes rather than log text — panicked as well.
value is optional on KeyValue, which hit the span attribute path.
Absent now converts to a zero value rather than crashing: an empty
resource, and an empty string for a missing body or attribute value,
which is what an explicitly empty one already produced. The nil guards
sit ahead of the type switches, so payloads that do set these fields are
unaffected. Absent also no longer falls into attrValue's "UNHANDLED ATTR
TYPE" branch, since absent is not unhandled.
Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
|
🤖 Corroboration from the consumer side, in case it's useful for the review: both of the first two were hit as real
Worth noting the second one isn't only reachable from new code:
|
The nil Resource / nil Body dereferences the import guards against are fixed at the decode boundary by dagger/otel-go#17, which also caught a third on the span-attribute side (attrValue, reachable because AttributesFromProto skips nil elements but passes a nil Value through). Mark the local guards as a stopgap to delete with the otel-go bump, so they do not outlive their reason, and record in §13.4 that the panic was never resume-specific: the engine's own POST /v1/logs handler feeds ReexportLogsFromPB directly.
Records what building the fetch ratified and what it changed: the sink interface that keeps internal/cloud transport-only, the sequential-streams decision and the race-detector evidence behind it, the seal's position between the span and log streams, the three places the reference implementation's error handling was wrong for a restore, and the two auth/URL bugs fixed on the way. Also notes that dagger/otel-go#17 has not merged, so slice 4's stopgap guards stay, and that §12's two curls still could not be run — nothing is deployed to curl — which leaves the fake server's fidelity as the slice's whole risk.
Three functions in
transform.godereferenced a pointer that the OTLP proto marks optional, so converting a perfectly valid payload that simply omits the field panicked with a nil pointer dereference.ResourceFromPBreadpb.Attributesdirectly.resourceis optional onResourceSpans,ResourceLogsandResourceMetrics— "if this field is not set then no resource info is known" — so a payload without one crashed. This reached users throughSpansFromPB(viareadOnlySpan.Resource()),ReexportLogsFromPBandResourceMetricsFromPB. Now uses the generated nil-safe getter,pb.GetAttributes().LogValueFromPBswitched onv.Value, which panics whenvis nil.bodyis optional onLogRecord, and an attribute-only record — one carrying its data in attributes rather than log text — is legal and common;ReexportLogsFromPBcallsLogValueFromPB(rec.GetBody())unconditionally. Now returnslog.StringValue(""), which is what an absent body means to every consumer and matches what a body explicitly set to""already produced. This also coverslogKeyValue, which passesv.GetValue().attrValuehad the identical bug on the span/attribute side, hit by aKeyValuewhosevaluefield is absent. Fixed the same way, returning an empty string value rather than falling into thedefaultbranch'sUNHANDLED ATTR TYPElogging — absent is not unhandled, it is absent.All three guards sit ahead of the type switch, so no payload that does set these fields changes behaviour. This follows the existing pattern in the file:
InstrumentationScopeFromPBandStatusCodeFromPBalready return a zero value for nil.Tests
Three regression tests in
transform_test.go, matching the existing testify style:TestSpansFromPBNilResource—ResourceSpans{Resource: nil}with one span, asserting.Resource()returns a non-nil resource with no attributes.TestReexportLogsFromPBNilResourceAndBody— nil resource, nil body, two attributes; asserts the exported record comes through with an empty body and both attributes intact.TestAttributesFromProtoNilValue— aKeyValuewith a nilValueconverts to an emptySTRINGattribute with its key preserved.Each one panics on its own with the fix reverted. Reverting only the
LogValueFromPBguard still makes the log test panic, confirming it covers the nil-body case independently rather than just tripping the resource bug first.go test ./...andgo vet ./...both pass.🤖 Generated with Claude Code