canopy-network/canopy · warning · ErrFieldNotFound

ErrFieldNotFound

ErrFieldNotFound

Error message

%w: %d

What it means

If the scan completes without finding the requested field number, GetRawProtoField returns ErrFieldNotFound wrapped with the field number. Per the library docs, in proto3 an absent scalar field equals its zero value, so this is often expected behavior rather than corruption — callers are expected to check with errors.Is.

Source

Thrown at lib/codec/codec.go:118

					return nil, fmt.Errorf("invalid field value at offset %d", offset)
				}
				if offset+valueLen > len(protoBytes) {
					return nil, fmt.Errorf("field value exceeds buffer bounds")
				}
				fieldBytes := make([]byte, valueLen)
				copy(fieldBytes, protoBytes[offset:offset+valueLen])
				return fieldBytes, nil
			}
		} else {
			// skip this field
			skipLen := protowire.ConsumeFieldValue(fieldNum, wireType, protoBytes[offset:])
			if skipLen < 0 {
				return nil, fmt.Errorf("invalid field value at offset %d", offset)
			}
			offset += skipLen
		}
	}
	return nil, fmt.Errorf("%w: %d", ErrFieldNotFound, fieldNumber)
}

// NullifyProtoField removes a field from protobytes without unmarshalling
func NullifyProtoField(protoBytes []byte, fieldNumber int) ([]byte, error) {
	var offset int
	// create a buffer to store the result
	result := make([]byte, 0, len(protoBytes))
	// iterate through the bytes
	for offset < len(protoBytes) {
		// remember the start position of this field
		fieldStart := offset
		// decode the field tag
		fieldNum, wireType, tagLen := protowire.ConsumeTag(protoBytes[offset:])
		if tagLen < 0 {
			return nil, fmt.Errorf("invalid tag at offset %d", offset)
		}
		// update the offset
		offset += tagLen

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Check for this error with errors.Is(err, codec.ErrFieldNotFound) and treat the field as its zero value when appropriate.
  2. Verify the field number matches the current .proto definition of the message.
  3. If the field must always exist, use proto2-style optional/required semantics or explicitly set the field before marshaling so it appears on the wire.

Example fix

// before
raw, err := codec.GetRawProtoField(data, 1)
if err != nil { return err }
// after
raw, err := codec.GetRawProtoField(data, 1)
if errors.Is(err, codec.ErrFieldNotFound) {
    return zeroValue, nil // proto3 zero-valued field
}
if err != nil { return err }
Defensive patterns

Strategy: fallback

Validate before calling

var msg pb.Event
if err := proto.Unmarshal(data, &msg); err != nil { return err }
// check proto3 field presence via getter/oneof or optional before raw extraction

Try / catch

raw, err := codec.GetRawProtoField(data, fieldNum)
if errors.Is(err, codec.ErrFieldNotFound) {
    return zeroValue, nil // proto3 zero-valued scalar: treat as empty
}
if err != nil { return err }

Prevention

When it happens

Trigger: GetRawProtoField (directly, or via entryKeyOrZero / BytesToBlockHash) on a message whose serialization omits the requested field — proto3 zero-valued scalars, unset optional fields, or a wrong field number passed by the caller.

Common situations: Proto3 default-zero fields (e.g. Pool{Id:0}, Account{Address:nil}) not emitted on the wire; asking for a field number from a different message version; messages serialized by an older schema without that field.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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