{"record":{"id":"b83795a73898a9f1","repo":"hyperledger/fabric","slug":"invalid-channel-less-operation","errorCode":null,"errorMessage":"invalid channel-less operation","messagePattern":"invalid channel-less operation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/chaincode/lifecycle/ledger_shim.go","lineNumber":189,"sourceCode":"\tState      PrivateQueryExecutor\n}\n\nfunc (pqes *PrivateQueryExecutorShim) GetStateHash(key string) ([]byte, error) {\n\treturn pqes.State.GetPrivateDataHash(pqes.Namespace, pqes.Collection, key)\n}\n\nfunc (pqes *PrivateQueryExecutorShim) CollectionName() string {\n\treturn pqes.Collection\n}\n\n// DummyQueryExecutorShim implements the ReadableState interface. It is\n// used to ensure channel-less system chaincode calls don't panic and return\n// and error when an invalid operation is attempted (i.e. an InstallChaincode\n// invocation against a chaincode other than _lifecycle)\ntype DummyQueryExecutorShim struct{}\n\nfunc (*DummyQueryExecutorShim) GetState(key string) ([]byte, error) {\n\treturn nil, errors.New(\"invalid channel-less operation\")\n}\n","sourceCodeStart":171,"sourceCodeEnd":191,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/chaincode/lifecycle/ledger_shim.go#L171-L191","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: channel-less shim used for state read\nshim := &lifecycle.DummyQueryExecutorShim{}\nval, _ := shim.GetState(key) // always errors\n// after: obtain executor from a real channel\nqe, err := ledger.GetQueryExecutorForChannel(channelID)\nval, err = qe.GetState(namespace, key)","handlingStrategy":"validation","validationCode":"// Go: only route channel-less lifecycle calls to _lifecycle\nfunc checkChannelLess(cc string) error {\n  if cc != \"_lifecycle\" {\n    return fmt.Errorf(\"channel-less operation only valid for _lifecycle, got %s\", cc)\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":"if err != nil && err.Error() == \"invalid channel-less operation\" {\n  // retry via a channel-scoped executor: ledger.GetQueryExecutorForChannel(channelID)\n}","preventionTips":["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."],"tags":["hyperledger-fabric","lifecycle","query-executor","system-chaincode","channel-less"],"backgroundTag":"invalid-channel-less-operation","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}