grpc/grpc-go · error

protocol %v is already registered

Error message

protocol %v is already registered

What it means

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().

Source

Thrown at credentials/alts/internal/conn/record.go:108

	pool, err := imem.NewDirtyBinaryTieredBufferPool(
		8,
		12, // Go page size, 4KB
		14, // 16KB (max HTTP/2 frame size used by gRPC)
		15, // 32KB (default buffer size for gRPC)
		16, // 64KB
		17, // 128KB
		19, // 512KB, max write buffer size
	)
	if err != nil {
		panic(fmt.Sprintf("Failed to create write buffer pool: %v", err))
	}
	writeBufPool = pool
}

// RegisterProtocol register a ALTS record encryption protocol.
func RegisterProtocol(protocol string, f ALTSRecordFunc) error {
	if _, ok := protocols[protocol]; ok {
		return fmt.Errorf("protocol %v is already registered", protocol)
	}
	protocols[protocol] = f
	return nil
}

// conn represents a secured connection. It implements the net.Conn interface.
type conn struct {
	net.Conn
	reader readyreader.Reader
	crypto ALTSRecordCrypto
	// buf holds data that has been read from the connection and decrypted,
	// but has not yet been returned by Read. It is a sub-slice of protected.
	buf                []byte
	payloadLengthLimit int
	// protectedHandle buffer holds data read from the network but have not yet
	// been decrypted. This data might not compose a complete frame.
	//
	// The buffer pointer to points to a buffer from the readBufPool. The handle

View on GitHub (pinned to 03255a9237)

Solutions

  1. Avoid registering a protocol name more than once; rely on the package's own init() registration.
  2. If you have a vendored duplicate, deduplicate the dependency (go mod tidy / replace) so init() runs once.
  3. In tests, check protocols map membership before registering, or reset the registry in TestMain.

Example fix

// before
conn.RegisterProtocol("ALTSRP_GCM", newGCM)
// ... later in another init()
conn.RegisterProtocol("ALTSRP_GCM", newGCM)  // error
// after
// remove the duplicate registration; rely on the canonical init()
Defensive patterns

Strategy: validation

Validate before calling

// Guard against duplicate registration in tests or libraries.
func safeRegisterProtocol(name string, f conn.ALTSRecordFunc) error {
    // no public getter; in tests, reset the map in TestMain or track names yourself
    return conn.RegisterProtocol(name, f)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/402deb1f064b2f34. Report an issue: GitHub.