canopy-network/canopy · error

unable to marshal any payload type %s to json

Error message

unable to marshal any payload type %s to json

What it means

MarshalAnypbJSON resolves a protobuf Any's TypeUrl against a registry of known message types and marshals the inner message to JSON. If the payload type cannot be resolved or marshaled after exhausting the registry, it returns 'unable to marshal any payload type %s to json'.

Source

Thrown at lib/codec.go:45

		if err := proto.Unmarshal(any.Value, dynamic); err == nil {
			jsonBytes, e := protojson.MarshalOptions{}.Marshal(dynamic)
			if e == nil {
				return jsonBytes, nil
			}
		}
	}
	// fallback to standard anypb.UnmarshalNew()
	payload, payloadErr := FromAny(any)
	if payloadErr == nil {
		if msgI, ok := payload.(MessageI); ok {
			msg, err := MarshalJSON(msgI)
			if err == nil {
				return msg, nil
			}
		}
	}
	// exit
	return nil, fmt.Errorf("unable to marshal any payload type %s to json", any.TypeUrl)
}

// MarshalAnyProtoJSON() converts an 'any proto' into JSON
func MarshalAnyProtoJSON(any *anypb.Any) (json.RawMessage, error) {
	if any == nil {
		return nil, nil
	}
	jsonBytes, err := protojson.MarshalOptions{}.Marshal(any)
	if err != nil {
		return nil, ErrJSONMarshal(err)
	}
	return jsonBytes, nil
}

// AnyFromProtoJSON() converts JSON into a protobuf.Any
func AnyFromProtoJSON(msg json.RawMessage) (a *anypb.Any, e ErrorI) {
	if len(msg) == 0 {
		return nil, ErrJSONUnmarshal(fmt.Errorf("empty json payload"))

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Register the missing message type in the codec/registry so its TypeUrl resolves
  2. Verify the Any's TypeUrl spelling matches the registered fully-qualified proto name
  3. Rebuild/upgrade the binary so it knows the payload type produced by the counterparty

Example fix

// before
any := &anypb.Any{TypeUrl: "type.googleapis.com/custom.v1.MyParam", ...} // never registered
// after
RegisterType(&customv1.MyParam{}) // or use a registered wrapper like StringWrapper
Defensive patterns

Strategy: validation

Validate before calling

if !codec.IsRegisteredType(any.TypeUrl) {
    return fmt.Errorf("type %s not registered", any.TypeUrl)
}

Try / catch

jsonRaw, err := lib.MarshalAnypbJSON(any)
if err != nil {
    // fall back to raw base64 of any.Value or reject the message
}

Prevention

When it happens

Trigger: MarshalAnypbJSON is called (e.g. from MessageChangeParameter-style MarshalJSON paths) with an Any whose TypeUrl is not registered, or whose resolved message fails protojson marshaling, so the loop over candidate types exits without success.

Common situations: A plugin/proto message type was added but never registered in the codec's type registry; version skew where a node produces a type an older binary doesn't know; typo in the type URL.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/8bb4241d5f9c22ad. Report an issue: GitHub.