temporalio/temporal · error · serializer error

%w: cannot deserialize into %v

Error message

%w: cannot deserialize into %v

What it means

The Temporal payloadSerializer implements nexus.Serializer and only accepts a **commonpb.Payload as the Deserialize output target, because it converts Nexus Content into Temporal payloads. Passing any other pointer type (e.g. **nexus.Content for a different serializer contract, or a struct pointer) is a type mismatch and returns this wrapped errSerializer.

Source

Thrown at common/nexus/payload_serializer.go:23

	"fmt"
	"maps"
	"mime"

	"github.com/nexus-rpc/sdk-go/nexus"
	commonpb "go.temporal.io/api/common/v1"
	enumspb "go.temporal.io/api/enums/v1"
	"go.temporal.io/server/common/persistence/serialization"
)

type payloadSerializer struct{}

var errSerializer = errors.New("serializer error")

// Deserialize implements nexus.Serializer.
func (payloadSerializer) Deserialize(content *nexus.Content, v any) error {
	payloadRef, ok := v.(**commonpb.Payload)
	if !ok {
		return fmt.Errorf("%w: cannot deserialize into %v", errSerializer, v)
	}

	payload := &commonpb.Payload{}
	*payloadRef = payload
	payload.Metadata = make(map[string][]byte)
	payload.Data = content.Data

	h := maps.Clone(content.Header)
	// We assume that encoding is handled by the transport layer and the content is decoded.
	delete(h, "encoding")
	// Length can safely be ignored.
	delete(h, "length")

	if len(h) > 1 {
		setUnknownNexusContent(h, payload.Metadata)
		return nil
	}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Pass a **commonpb.Payload as the output parameter when using the Temporal payload serializer.
  2. Check which Serializer is registered in nexus HandlerServerOptions and use the matching input/output types in handlers.
  3. If handlers should use plain Go types, use the default nexus.Serializer instead of the Temporal one.
  4. Inspect the wrapping error's %v suffix — it prints the actual value passed — to spot the type mismatch.

Example fix

// before
var out string
err := serializer.Deserialize(content, &out) // not **commonpb.Payload
// after
var payload *commonpb.Payload
err := serializer.Deserialize(content, &payload)
Defensive patterns

Strategy: type-guard

Validate before calling

func canDeserializeWithTemporalSerializer(v any) bool {
    _, ok := v.(**commonpb.Payload)
    return ok
}

Type guard

func deserializePayload(content *nexus.Content) (*commonpb.Payload, error) {
    var p *commonpb.Payload
    if err := payloadSerializer{}.Deserialize(content, &p); err != nil {
        return nil, err
    }
    return p, nil
}

Try / catch

var payload *commonpb.Payload
if err := serializer.Deserialize(content, &payload); err != nil {
    return nil, fmt.Errorf("nexus input is not a temporal payload: %w", err)
}

Prevention

When it happens

Trigger: Calling payloadSerializer.Deserialize(content, v) where v is not **commonpb.Payload — e.g. passing *string, **struct, or a *nexus.Content target — typically from the anonymous handler plumbing that deserializes Nexus operation inputs.

Common situations: Mixing serializers: using the Temporal payload serializer with handlers written for the default nexus.Serializer (or vice versa); misconfigured Server options that install the wrong serializer; hand-written test code calling Deserialize with the wrong target type.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/7df118f9537ccb62. Report an issue: GitHub.