hyperledger/fabric · warning

could not find handler

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.

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.

Appendix: 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)