{"record":{"id":"a146d389fcd3cb4e","repo":"canopy-network/canopy","slug":"field-value-exceeds-buffer-bounds","errorCode":null,"errorMessage":"field value exceeds buffer bounds","messagePattern":"field value exceeds buffer bounds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/codec/codec.go","lineNumber":88,"sourceCode":"\t\tfieldNum, wireType, tagLen := protowire.ConsumeTag(protoBytes[offset:])\n\t\tif tagLen < 0 {\n\t\t\treturn nil, fmt.Errorf(\"invalid tag at offset %d\", offset)\n\t\t}\n\t\toffset += tagLen\n\t\t// check if this is the field we're looking for\n\t\tif int(fieldNum) == fieldNumber {\n\t\t\t// for length-delimited fields (like messages), we need to read the length\n\t\t\tif wireType == protowire.BytesType {\n\t\t\t\t// read the length of the field value\n\t\t\t\tvalueLen, lenBytes := protowire.ConsumeVarint(protoBytes[offset:])\n\t\t\t\tif lenBytes < 0 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"invalid length at offset %d\", offset)\n\t\t\t\t}\n\t\t\t\t// calculate the new offset\n\t\t\t\toffset += lenBytes\n\t\t\t\t// extract the field value bytes\n\t\t\t\tif offset+int(valueLen) > len(protoBytes) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"field value exceeds buffer bounds\")\n\t\t\t\t}\n\t\t\t\t// make buffer to return\n\t\t\t\tfieldBytes := make([]byte, valueLen)\n\t\t\t\t// copy into the buffer\n\t\t\t\tcopy(fieldBytes, protoBytes[offset:offset+int(valueLen)])\n\t\t\t\t// return the value\n\t\t\t\treturn fieldBytes, nil\n\t\t\t} else {\n\t\t\t\t// for other wire types, consume the value directly\n\t\t\t\tvalueLen := protowire.ConsumeFieldValue(fieldNum, wireType, protoBytes[offset:])\n\t\t\t\tif valueLen < 0 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"invalid field value at offset %d\", offset)\n\t\t\t\t}\n\t\t\t\tif offset+valueLen > len(protoBytes) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"field value exceeds buffer bounds\")\n\t\t\t\t}\n\t\t\t\tfieldBytes := make([]byte, valueLen)\n\t\t\t\tcopy(fieldBytes, protoBytes[offset:offset+valueLen])","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/lib/codec/codec.go#L70-L106","documentation":"After reading the length prefix of a matching BytesType field, GetRawProtoField checks that offset+valueLen stays within the buffer. If the declared length runs past the end of the bytes, the payload is truncated or corrupt, so extraction stops rather than reading out of bounds.","triggerScenarios":"GetRawProtoField on a buffer where the target field's varint length claims more bytes than actually remain — truncated or mutated wire data.","commonSituations":"Storage/network truncation; manually edited proto bytes; mixing bytes from two different messages; a length varint misread because the buffer was sliced mid-field.","solutions":["Confirm the buffer is the complete, unmodified output of proto.Marshal for the message.","Run proto.Unmarshal on the full buffer as a validity gate before raw field extraction.","If the bytes are composed from multiple sources, ensure field boundaries are not being cut."],"exampleFix":"// before\nraw, _ := codec.GetRawProtoField(partialBlob, 2)\n// after\nvar m mypb.Event\nif err := proto.Unmarshal(fullBlob, &m); err != nil { return err }\nraw, err := codec.GetRawProtoField(fullBlob, 2)","handlingStrategy":"validation","validationCode":"var probe pb.Event\nif err := proto.Unmarshal(data, &probe); err != nil {\n    return fmt.Errorf(\"buffer failed proto validation: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"raw, err := codec.GetRawProtoField(data, fieldNum)\nif err != nil && strings.Contains(err.Error(), \"exceeds buffer bounds\") {\n    return fmt.Errorf(\"truncated field payload: %w\", err)\n}","preventionTips":["Pass complete marshaled messages; avoid trimming or chunking before extraction.","Detect truncation at the IO layer (n vs expected length) before parsing.","Never splice bytes from different messages together."],"tags":["protobuf","wire-format","truncated-data","bounds"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}