hyperledger/fabric · warning

could not find handler: %s

Error message

could not find handler: %s

What it means

Returned by HandlerRegistry.Deregister when no handler exists for the given chaincodeID in the registry. The peer looked up r.handlers[ccid], found nil, and refuses to close/deregister a handler that was never (or no longer) registered.

Source

Thrown at core/chaincode/handler_registry.go:163

	chaincodeLogger.Debugf("registered handler complete for chaincode %s", h.chaincodeID)
	return nil
}

// Deregister clears references to state associated specified chaincode.
// As part of the cleanup, it closes the handler so it can cleanup any state.
// If the registry does not contain the provided handler, an error is returned.
func (r *HandlerRegistry) Deregister(ccid string) error {
	chaincodeLogger.Debugf("deregister handler: %s", ccid)

	r.mutex.Lock()
	handler := r.handlers[ccid]
	delete(r.handlers, ccid)
	delete(r.launching, ccid)
	r.mutex.Unlock()

	if handler == nil {
		return errors.Errorf("could not find handler: %s", ccid)
	}

	handler.Close()

	chaincodeLogger.Debugf("deregistered handler with key: %s", ccid)
	return nil
}

type TxQueryExecutorGetter struct {
	HandlerRegistry *HandlerRegistry
	CCID            string
}

func (g *TxQueryExecutorGetter) TxQueryExecutor(chainID, txID string) ledger.SimpleQueryExecutor {
	handler := g.HandlerRegistry.Handler(g.CCID)
	if handler == nil {
		return nil
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify the chaincodeID passed to Deregister matches one that is currently registered.
  2. Treat the error as benign in cleanup paths — log and continue if the handler is already gone.
  3. Check for double-close/double-deregister logic in the shutdown path and guard it.
  4. Capture peer logs around chaincode exit to find why the handler was missing (crash before full registration).

Example fix

// cleanup path
// before
if err := registry.Deregister(ccid); err != nil { return err }
// after
if err := registry.Deregister(ccid); err != nil {
    logger.Debugf("deregister skipped: %v", err) // handler already gone
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := registry.Deregister(ccid); err != nil && strings.Contains(err.Error(), "could not find handler") {
    // benign: handler already gone; log at debug and continue cleanup
}

Prevention

When it happens

Trigger: Deregister(ccid) is called for a chaincodeID not present in r.handlers — e.g. double deregistration, deregistering a chaincode that never registered, or after the registry entry was already deleted.

Common situations: Chaincode connection closes twice (handler cleanup race); peer restarting while a chaincode exits and calls deregister; a launch failure path deregistering a handler that failed registration; tests calling Deregister with a fabricated chaincodeID.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/e7abb4da43e44e38. Report an issue: GitHub.