temporalio/temporal · error · serializer error
%w: payload marshal error: %w
Error message
%w: payload marshal error: %w
What it means
xTemporalPayload converts a *commonpb.Payload into Nexus Content by calling payload.Marshal() (proto marshal). If the proto message fails to marshal — e.g. it contains data that violates proto3 invariants — the error is wrapped as errSerializer with this message and propagated to Serialize's caller.
Source
Thrown at common/nexus/payload_serializer.go:166
return xTemporalPayload(payload)
}
// type is unset
case "binary/plain":
if len(payload.Metadata) != 1 {
return xTemporalPayload(payload)
}
content.Header["type"] = "application/octet-stream"
default:
return xTemporalPayload(payload)
}
return &content, nil
}
func xTemporalPayload(payload *commonpb.Payload) (*nexus.Content, error) {
data, err := payload.Marshal()
if err != nil {
return nil, fmt.Errorf("%w: payload marshal error: %w", errSerializer, err)
}
return &nexus.Content{
Header: nexus.Header{"type": "application/x-temporal-payload"},
Data: data,
}, nil
}
var PayloadSerializer nexus.Serializer = payloadSerializer{}
View on GitHub (pinned to bde624efd1)
Solutions
- Log the inner marshal error to identify which field of the Payload is invalid.
- Rebuild the Payload through Temporal's data converter APIs instead of hand-assembling it.
- Validate the payload (Metadata keys, encoding, Data) before serializing.
- Ensure the proto runtime versions match across dependencies (protoimpl conflicts).
Example fix
// before
payload := &commonpb.Payload{Metadata: nil, Data: data} // built by hand, fails marshal
// after
payload, err := converter.ToPayload(value) // build via data converter
if err != nil {
return err
}
content, err := serializer.Serialize(payload) Defensive patterns
Strategy: validation
Validate before calling
if payload == nil || len(payload.Metadata) == 0 && len(payload.Data) == 0 {
return errors.New("refusing to serialize empty or hand-built payload")
} Type guard
func isMarshalablePayload(p *commonpb.Payload) bool {
return p != nil && proto.CheckInitialized(p) == nil
} Try / catch
content, err := serializer.Serialize(payload)
if err != nil && errors.Is(err, errSerializer) {
return nil, fmt.Errorf("payload produced by %s is corrupt: %w", origin, err)
} Prevention
- Build payloads via Temporal data converters instead of hand-assembling structs
- Validate payloads read from persistence before re-serializing
- Keep protobuf runtime versions consistent across dependencies
- Round-trip payloads in tests (ToPayload -> FromPayload) to catch corruption early
When it happens
Trigger: Calling Serialize with a *commonpb.Payload whose Marshal() returns an error — typically a payload with unknown/invalid enum values or a corrupt required state produced by manually assembling Metadata/Data.
Common situations: Manually constructed Payload structs with invalid fields; payloads deserialized from corrupt persistence or external sources; proto runtime version incompatibilities producing unencodable messages.
Related errors
- serializer error
- cannot serialize HSM task. unable to cast to expected type
- failed to serialize handler result: %w
- %w: cannot deserialize into %v
- %w: cannot serialize %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/0f4929fdde8e9a8a.
Report an issue: GitHub.