{"record":{"id":"7870bdec8977fc14","repo":"dagger/dagger","slug":"failed-to-json-unmarshal-w","errorCode":null,"errorMessage":"failed to json unmarshal: %w","messagePattern":"failed to json unmarshal: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/clientdb/span.go","lineNumber":103,"sourceCode":"}\n\n// StartTime returns the start time of the span\nfunc (ros *readOnlySpan) StartTime() time.Time {\n\treturn time.Unix(0, ros.DB.StartTime)\n}\n\n// EndTime returns the end time of the span\nfunc (ros *readOnlySpan) EndTime() time.Time {\n\tif ros.DB.EndTime.Valid {\n\t\treturn time.Unix(0, ros.DB.EndTime.Int64)\n\t}\n\treturn time.Time{}\n}\n\nfunc UnmarshalProtoJSONs[T proto.Message](pb []byte, base T, out *[]T) error {\n\tvar msgs []json.RawMessage\n\tif err := json.Unmarshal(pb, &msgs); err != nil {\n\t\treturn fmt.Errorf(\"failed to json unmarshal: %w\", err)\n\t}\n\tprotos := make([]T, len(msgs))\n\tfor i, msg := range msgs {\n\t\tpl := proto.Clone(base).(T)\n\t\tif err := protojson.Unmarshal(msg, pl); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to protojson unmarshal %s into %T: %w\", msg, pl, err)\n\t\t}\n\t\tprotos[i] = pl\n\t}\n\t*out = protos\n\treturn nil\n}\n\nfunc MarshalProtoJSONs[T proto.Message](protos []T) ([]byte, error) {\n\tmsgs := make([]json.RawMessage, len(protos))\n\tfor i, msg := range protos {\n\t\tpl, err := protojson.Marshal(msg)\n\t\tif err != nil {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/engine/clientdb/span.go#L85-L121","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nerr := UnmarshalProtoJSONs(rawBlob, baseProto, &out) // rawBlob is proto binary\n// after\njsonBytes, err := MarshalProtoJSONs(msgs) // persist JSON-array form\nif err != nil { return err }\nerr = UnmarshalProtoJSONs(jsonBytes, baseProto, &out)","handlingStrategy":"validation","validationCode":"if len(pb) == 0 || !json.Valid(pb) {\n    return fmt.Errorf(\"payload is not valid JSON (%d bytes)\", len(pb))\n}\nvar probe []json.RawMessage\nif err := json.Unmarshal(pb, &probe); err != nil {\n    return fmt.Errorf(\"payload is not a JSON array: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"var out []pb.Span\nif err := UnmarshalProtoJSONs(raw, base, &out); err != nil {\n    log.Warn(\"unparseable span payload, skipping\", \"err\", err, \"bytes\", len(raw))\n    return nil // degrade gracefully instead of failing the query\n}","preventionTips":["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."],"tags":["go","json","serialization","protojson"],"backgroundTag":"invalid-json-payload","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}