hyperledger/fabric · error
invalid channel-less operation
Error message
invalid channel-less operation
What it means
DummyQueryExecutorShim is a placeholder query executor (per the source comment) used for channel-less system chaincode operations like _lifecycle's InstallChaincode; its GetState always returns this fixed error. It exists purely to make channel-less calls fail cleanly instead of panicking — e.g., an InstallChaincode invocation attempted against a chaincode other than _lifecycle.
Source
Thrown at core/chaincode/lifecycle/ledger_shim.go:189
State PrivateQueryExecutor
}
func (pqes *PrivateQueryExecutorShim) GetStateHash(key string) ([]byte, error) {
return pqes.State.GetPrivateDataHash(pqes.Namespace, pqes.Collection, key)
}
func (pqes *PrivateQueryExecutorShim) CollectionName() string {
return pqes.Collection
}
// DummyQueryExecutorShim implements the ReadableState interface. It is
// used to ensure channel-less system chaincode calls don't panic and return
// and error when an invalid operation is attempted (i.e. an InstallChaincode
// invocation against a chaincode other than _lifecycle)
type DummyQueryExecutorShim struct{}
func (*DummyQueryExecutorShim) GetState(key string) ([]byte, error) {
return nil, errors.New("invalid channel-less operation")
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Do not use DummyQueryExecutorShim for state-reading operations; route chaincode invocations through a real channel context (GetQueryExecutorForChannel).
- Ensure channel-less system chaincode calls target only _lifecycle and never attempt ledger reads.
- In application code, always invoke chaincode with a valid channelID so a real query executor backs state access.
Example fix
// before: channel-less shim used for state read
shim := &lifecycle.DummyQueryExecutorShim{}
val, _ := shim.GetState(key) // always errors
// after: obtain executor from a real channel
qe, err := ledger.GetQueryExecutorForChannel(channelID)
val, err = qe.GetState(namespace, key) Defensive patterns
Strategy: validation
Validate before calling
// Go: only route channel-less lifecycle calls to _lifecycle
func checkChannelLess(cc string) error {
if cc != "_lifecycle" {
return fmt.Errorf("channel-less operation only valid for _lifecycle, got %s", cc)
}
return nil
} Try / catch
if err != nil && err.Error() == "invalid channel-less operation" {
// retry via a channel-scoped executor: ledger.GetQueryExecutorForChannel(channelID)
} Prevention
- Never perform ledger reads through DummyQueryExecutorShim.
- Channel-less calls are only for _lifecycle InstallChaincode/GetChaincodePackage operations.
- In chaincode, always obtain state via the channel-scoped stub/query executor.
- Add assertions that shims are not used outside their intended channel-less paths.
When it happens
Trigger: Any GetState(key) call routed through DummyQueryExecutorShim — i.e., channel-less lifecycle code paths attempting to read ledger state, such as installing a non-_lifecycle chaincode in a channel-less context.
Common situations: Internal misuse of the lifecycle shim: calling chaincode that performs GetState without a channel context; installing/operating on a chaincode other than _lifecycle through channel-less APIs.
Related errors
- failed listing installed chaincodes
- failed to parse collection config
- [channel %s] failed to get chaincode container info for %s
- chaincode '%s' does not require initialization but called as
- private data APIs are not allowed in chaincode Init()
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/b83795a73898a9f1.
Report an issue: GitHub.