{"record":{"id":"448a42c6196f691c","repo":"canopy-network/canopy","slug":"invalid-length-at-offset-d","errorCode":null,"errorMessage":"invalid length at offset %d","messagePattern":"invalid length at offset (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/codec/codec.go","lineNumber":82,"sourceCode":"// GetRawProtoField extracts the raw bytes for field number from a proto message\nfunc GetRawProtoField(protoBytes []byte, fieldNumber int) ([]byte, error) {\n\tvar offset int\n\t// parse the proto bytes to find a field\n\tfor offset < len(protoBytes) {\n\t\t// decode the field tag (field number + wire type)\n\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)","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/lib/codec/codec.go#L64-L100","documentation":"When a matching length-delimited (BytesType) field is found, GetRawProtoField reads the value length as a varint. ConsumeVarint returning a negative byte count means the length varint itself is malformed/truncated — the buffer ends inside the varint, so the field's length cannot be determined.","triggerScenarios":"GetRawProtoField on a truncated proto buffer where the tag of the target field is present but the varint length prefix is cut off.","commonSituations":"Partial reads from storage or network producing truncated payloads; off-by-one slicing of the proto bytes; corruption during transport or persistence.","solutions":["Re-read or re-fetch the complete message bytes; confirm length matches the originally marshaled size.","Validate with proto.Unmarshal on the full buffer before calling GetRawProtoField to detect truncation early.","Fix the code that slices/passes a partial buffer to GetRawProtoField."],"exampleFix":"// before\nchunk := buf[:len(buf)-2] // accidental truncation\nraw, _ := codec.GetRawProtoField(chunk, 1)\n// after\nraw, err := codec.GetRawProtoField(buf, 1)","handlingStrategy":"validation","validationCode":"var probe pb.Event\nif err := proto.Unmarshal(data, &probe); err != nil {\n    return fmt.Errorf(\"truncated/malformed proto bytes: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"raw, err := codec.GetRawProtoField(data, fieldNum)\nif err != nil && strings.Contains(err.Error(), \"invalid length\") {\n    return fmt.Errorf(\"truncated buffer at length prefix: %w\", err)\n}","preventionTips":["Verify byte counts on storage/network reads before parsing.","Never pass manually sliced fragments of a message to raw field extraction.","Include integrity checks (length headers, checksums) on persisted blobs."],"tags":["protobuf","wire-format","truncated-data","varint"],"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-14T00:17:10.932Z"}