dagger/dagger · error
failed to json unmarshal: %w
Error message
failed to json unmarshal: %w
What it means
UnmarshalProtoJSONs in engine/clientdb/span.go decodes a JSON array of protobuf messages (span attributes, log payloads) stored in the client-side trace database. Before it can protojson-decode each element, the outer payload must itself be a valid JSON array of raw JSON values. This error wraps the encoding/json failure when the stored bytes are not parseable JSON at all, so the function aborts before touching any protobuf message.
Source
Thrown at engine/clientdb/span.go:103
}
// StartTime returns the start time of the span
func (ros *readOnlySpan) StartTime() time.Time {
return time.Unix(0, ros.DB.StartTime)
}
// EndTime returns the end time of the span
func (ros *readOnlySpan) EndTime() time.Time {
if ros.DB.EndTime.Valid {
return time.Unix(0, ros.DB.EndTime.Int64)
}
return time.Time{}
}
func UnmarshalProtoJSONs[T proto.Message](pb []byte, base T, out *[]T) error {
var msgs []json.RawMessage
if err := json.Unmarshal(pb, &msgs); err != nil {
return fmt.Errorf("failed to json unmarshal: %w", err)
}
protos := make([]T, len(msgs))
for i, msg := range msgs {
pl := proto.Clone(base).(T)
if err := protojson.Unmarshal(msg, pl); err != nil {
return fmt.Errorf("failed to protojson unmarshal %s into %T: %w", msg, pl, err)
}
protos[i] = pl
}
*out = protos
return nil
}
func MarshalProtoJSONs[T proto.Message](protos []T) ([]byte, error) {
msgs := make([]json.RawMessage, len(protos))
for i, msg := range protos {
pl, err := protojson.Marshal(msg)
if err != nil {View on GitHub (pinned to 82ba2681db)
Solutions
- Log the raw pb bytes (hex/utf-8 preview) and run them through json.Valid to confirm the payload is actually JSON.
- Verify the bytes were written by MarshalProtoJSONs; if the source stores proto binary, switch the writer to MarshalProtoJSONs or decode with proto.Unmarshal instead.
- Check for truncation: compare stored blob length to what the writer produced; re-export/re-ingest the affected spans.
- Upgrade/migrate old rows written by a previous schema version to the current JSON-array format.
Example fix
// before
err := UnmarshalProtoJSONs(rawBlob, baseProto, &out) // rawBlob is proto binary
// after
jsonBytes, err := MarshalProtoJSONs(msgs) // persist JSON-array form
if err != nil { return err }
err = UnmarshalProtoJSONs(jsonBytes, baseProto, &out) Defensive patterns
Strategy: validation
Validate before calling
if len(pb) == 0 || !json.Valid(pb) {
return fmt.Errorf("payload is not valid JSON (%d bytes)", len(pb))
}
var probe []json.RawMessage
if err := json.Unmarshal(pb, &probe); err != nil {
return fmt.Errorf("payload is not a JSON array: %w", err)
} Try / catch
var out []pb.Span
if err := UnmarshalProtoJSONs(raw, base, &out); err != nil {
log.Warn("unparseable span payload, skipping", "err", err, "bytes", len(raw))
return nil // degrade gracefully instead of failing the query
} Prevention
- Always persist protos via MarshalProtoJSONs so reader and writer formats match.
- Never store proto binary wire format in columns decoded by UnmarshalProtoJSONs.
- Run json.Valid on payloads when migrating or copying rows between stores.
When it happens
Trigger: Calling UnmarshalProtoJSONs (directly or via LogsToPB, Attributes, captureLogLines, classifyLogSpan, beneathInternal, serviceInstallSpan) with pb bytes that are not valid JSON — e.g. empty slice, truncated data, a plain proto binary encoding instead of JSON, or hand-written rows in the DB that were never produced by MarshalProtoJSONs.
Common situations: Reading a span row written by an older schema or a different writer; a DB blob corrupted or truncated on disk; passing the proto binary wire format where the JSON-array format is expected; copying/migrating rows between databases with encoding loss (e.g. stored as TEXT and mangled).
Related errors
- decode persisted container withoutEntrypoint lazy payload: %
- decode persisted container withDefaultArgs lazy payload: %w
- decode persisted container withoutDefaultArgs lazy payload:
- decode persisted container withUser lazy payload: %w
- decode persisted container withoutUser lazy payload: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/7870bdec8977fc14.
Report an issue: GitHub.