{"record":{"id":"402deb1f064b2f34","repo":"grpc/grpc-go","slug":"protocol-v-is-already-registered","errorCode":null,"errorMessage":"protocol %v is already registered","messagePattern":"protocol (.+?) is already registered","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/alts/internal/conn/record.go","lineNumber":108,"sourceCode":"\tpool, err := imem.NewDirtyBinaryTieredBufferPool(\n\t\t8,\n\t\t12, // Go page size, 4KB\n\t\t14, // 16KB (max HTTP/2 frame size used by gRPC)\n\t\t15, // 32KB (default buffer size for gRPC)\n\t\t16, // 64KB\n\t\t17, // 128KB\n\t\t19, // 512KB, max write buffer size\n\t)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Failed to create write buffer pool: %v\", err))\n\t}\n\twriteBufPool = pool\n}\n\n// RegisterProtocol register a ALTS record encryption protocol.\nfunc RegisterProtocol(protocol string, f ALTSRecordFunc) error {\n\tif _, ok := protocols[protocol]; ok {\n\t\treturn fmt.Errorf(\"protocol %v is already registered\", protocol)\n\t}\n\tprotocols[protocol] = f\n\treturn nil\n}\n\n// conn represents a secured connection. It implements the net.Conn interface.\ntype conn struct {\n\tnet.Conn\n\treader readyreader.Reader\n\tcrypto ALTSRecordCrypto\n\t// buf holds data that has been read from the connection and decrypted,\n\t// but has not yet been returned by Read. It is a sub-slice of protected.\n\tbuf                []byte\n\tpayloadLengthLimit int\n\t// protectedHandle buffer holds data read from the network but have not yet\n\t// been decrypted. This data might not compose a complete frame.\n\t//\n\t// The buffer pointer to points to a buffer from the readBufPool. The handle","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/alts/internal/conn/record.go#L90-L126","documentation":"RegisterProtocol (record.go:106-109) is the registry for ALTS record encryption protocols (e.g. ALTSRP_GCM). Registering a name that already exists in the protocols map returns this error to prevent silently shadowing a crypto implementation. This is normally called from package init().","triggerScenarios":"Calling conn.RegisterProtocol twice with the same name — typically because two init() paths both register the same protocol, or a library is imported twice / vendored in two copies.","commonSituations":"Double-vendoring of the ALTS conn package; a test binary that registers a protocol already registered by the production init(); a fork that re-registers on top of the upstream registration.","solutions":["Avoid registering a protocol name more than once; rely on the package's own init() registration.","If you have a vendored duplicate, deduplicate the dependency (go mod tidy / replace) so init() runs once.","In tests, check protocols map membership before registering, or reset the registry in TestMain."],"exampleFix":"// before\nconn.RegisterProtocol(\"ALTSRP_GCM\", newGCM)\n// ... later in another init()\nconn.RegisterProtocol(\"ALTSRP_GCM\", newGCM)  // error\n// after\n// remove the duplicate registration; rely on the canonical init()","handlingStrategy":"validation","validationCode":"// Guard against duplicate registration in tests or libraries.\nfunc safeRegisterProtocol(name string, f conn.ALTSRecordFunc) error {\n    // no public getter; in tests, reset the map in TestMain or track names yourself\n    return conn.RegisterProtocol(name, f)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register protocols only via package init(); don't re-register manually.","Deduplicate vendored copies of the alts/conn package.","In tests, isolate the registry to avoid cross-test double registration."],"tags":["go","grpc","alts","registry","init"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}