temporalio/temporal · error · serializer error

%w: cannot serialize %v

Error message

%w: cannot serialize %v

What it means

payloadSerializer.Serialize only accepts *commonpb.Payload (or nil) as the value to serialize into Nexus Content. Any other value type — a raw struct, string, map, etc. — is rejected with this wrapped errSerializer, mirroring the Nexus Go SDK's contract that the Temporal-specific serializer handles Temporal payloads exclusively.

Source

Thrown at common/nexus/payload_serializer.go:107

	return nil
}

func setUnknownNexusContent(nexusHeader nexus.Header, payloadMetadata map[string][]byte) {
	for k, v := range nexusHeader {
		payloadMetadata[k] = []byte(v)
	}
	payloadMetadata["encoding"] = []byte("unknown/nexus-content")
}

// Serialize implements nexus.Serializer.
func (payloadSerializer) Serialize(v any) (*nexus.Content, error) {
	if v == nil {
		// Use same structure as the nil serializer from the Nexus Go SDK.
		return &nexus.Content{Header: nexus.Header{}}, nil
	}
	payload, ok := v.(*commonpb.Payload)
	if !ok {
		return nil, fmt.Errorf("%w: cannot serialize %v", errSerializer, v)
	}

	// Use the "nil" Nexus Content representation for nil Payloads.
	if payload == nil {
		// Use same structure as the nil serializer from the Nexus Go SDK.
		return &nexus.Content{Header: nexus.Header{}}, nil
	}

	if len(payload.GetMetadata()) == 0 {
		return xTemporalPayload(payload)
	}

	content := nexus.Content{Header: nexus.Header{}, Data: payload.Data}
	encoding := string(payload.Metadata["encoding"])
	messageType := string(payload.Metadata["messageType"])

	switch encoding {
	case "unknown/nexus-content":

View on GitHub (pinned to bde624efd1)

Solutions

  1. Wrap the value in a *commonpb.Payload (Metadata + Data) before serializing.
  2. Use the default nexus.Serializer if you need to serialize arbitrary Go values.
  3. Align handler signatures with the configured serializer's supported types.
  4. Check the %v in the error message to identify the offending type.

Example fix

// before
content, err := serializer.Serialize("hello") // string not supported
// after
content, err := serializer.Serialize(&commonpb.Payload{
    Metadata: map[string][]byte{"encoding": []byte("json/plain")},
    Data:     []byte(`"hello"`),
})
Defensive patterns

Strategy: type-guard

Validate before calling

func canSerializeWithTemporalSerializer(v any) bool {
    if v == nil {
        return true
    }
    _, ok := v.(*commonpb.Payload)
    return ok
}

Type guard

func serializeValue(v any) (*nexus.Content, error) {
    if p, ok := v.(*commonpb.Payload); ok || v == nil {
        return payloadSerializer{}.Serialize(p)
    }
    return defaultSerializer{}.Serialize(v) // fall back for arbitrary types
}

Try / catch

content, err := serializer.Serialize(v)
if err != nil {
    return nil, nexus.HandlerErrorf(nexus.HandlerErrorTypeInternal, "unsupported result type %T", v)
}

Prevention

When it happens

Trigger: Calling Serialize(v) where v is not *commonpb.Payload — e.g. returning a plain struct or string from a Nexus handler whose server was configured with the Temporal payload serializer.

Common situations: Handler signatures using native Go types while the Temporal serializer is registered; test helpers calling Serialize with arbitrary values; refactors that change handler result types without updating serializer configuration.

Related errors


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