canopy-network/canopy · error · ErrJSONUnmarshal

ErrJSONUnmarshal

ErrJSONUnmarshal

Error message

empty json payload

What it means

Raised in AnyFromProtoJSON (lib/codec.go:63) when the input json.RawMessage is empty (len 0). There is no JSON to unmarshal into a protobuf.Any, so the conversion is rejected up front with ErrJSONUnmarshal instead of producing a nil/degenerate Any.

Source

Thrown at lib/codec.go:63

	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"))
	}
	a = new(anypb.Any)
	if err := protojson.Unmarshal(msg, a); err != nil {
		return nil, ErrJSONUnmarshal(err)
	}
	return a, nil
}

// AnyFromJSONForMessageType() converts JSON into anypb
func AnyFromJSONForMessageType(messageType string, msg json.RawMessage) (*anypb.Any, ErrorI) {
	if messageType == "" {
		return nil, ErrUnknownMessageName(messageType)
	}
	desc := globalPluginSchemaRegistry.FindMessageDescriptorForMessageType(messageType)
	typeURL := messageType
	if strings.Contains(messageType, "/") {
		desc = globalPluginSchemaRegistry.FindMessageDescriptorForTypeURL(messageType)
	} else if desc != nil {

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Ensure the JSON being unmarshaled contains a non-empty payload (check len before calling)
  2. Fix the producer so it omits the call entirely or emits at least '{}' instead of empty bytes
  3. Guard the caller: skip AnyFromProtoJSON when json.RawMessage is nil/empty

Example fix

// before
var msg json.RawMessage
a, err := lib.AnyFromProtoJSON(msg) // msg is empty
// after
if len(msg) == 0 {
    return fmt.Errorf("missing parameter payload")
}
a, err := lib.AnyFromProtoJSON(msg)
Defensive patterns

Strategy: validation

Validate before calling

if len(raw) == 0 {
    return errors.New("refusing to unmarshal empty json payload")
}

Type guard

func nonEmptyJSON(m json.RawMessage) bool { return len(m) > 0 }

Try / catch

a, err := lib.AnyFromProtoJSON(raw)
var eErr *fsm.ErrorI
if err != nil && strings.Contains(err.Error(), "empty json payload") {
    // treat as missing parameter field
}

Prevention

When it happens

Trigger: AnyFromProtoJSON is called from UnmarshalJSON with an empty (zero-length) json.RawMessage — e.g. the message's parameterValue/parameter bytes were empty or the field was omitted so nothing was passed through.

Common situations: A client submits a tx JSON with a missing/empty parameter field; upstream marshaling produced empty raw JSON; a nil value was serialized as an empty slice.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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