{"record":{"id":"b2e41eb40aa8ac63","repo":"hyperledger/fabric","slug":"missing-channel-id-b2e41e","errorCode":null,"errorMessage":"missing channel ID","messagePattern":"missing channel ID","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"orderer/common/channelparticipation/restapi.go","lineNumber":520,"sourceCode":"\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, \"cannot read file part %s from request body\", FormDataConfigUpdateEnvelopeKey))\n\t\treturn nil\n\t}\n\n\tenvelope := &cb.Envelope{}\n\terr = proto.Unmarshal(envelopeBytes, envelope)\n\tif err != nil {\n\t\th.logger.Debugf(\"Failed to unmarshal envelopeBytes: %s\", err)\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, \"cannot unmarshal file part %s into an envelope\", FormDataConfigUpdateEnvelopeKey))\n\t\treturn nil\n\t}\n\n\treturn envelope\n}\n\nfunc (h *HTTPHandler) extractChannelID(req *http.Request, resp http.ResponseWriter) (string, error) {\n\tchannelID, ok := mux.Vars(req)[channelIDKey]\n\tif !ok {\n\t\terr := errors.New(\"missing channel ID\")\n\t\th.sendResponseJsonError(resp, http.StatusInternalServerError, err)\n\t\treturn \"\", err\n\t}\n\n\tif err := configtx.ValidateChannelID(channelID); err != nil {\n\t\terr = errors.WithMessage(err, \"invalid channel ID\")\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, err)\n\t\treturn \"\", err\n\t}\n\treturn channelID, nil\n}\n\nfunc (h *HTTPHandler) extractBlockID(req *http.Request, resp http.ResponseWriter) (string, error) {\n\tblockID, ok := mux.Vars(req)[blockIDKey]\n\tif !ok {\n\t\terr := errors.New(\"missing block ID\")\n\t\th.sendResponseJsonError(resp, http.StatusInternalServerError, err)\n\t\treturn \"\", err","sourceCodeStart":502,"sourceCodeEnd":538,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/channelparticipation/restapi.go#L502-L538","documentation":"This error means the HTTP request reached the channel participation REST API without a channel ID path variable, so extractChannelID cannot identify which channel the request targets. The gorilla/mux router did not populate the channelIDKey variable in the request, which the handler treats as an internal routing misconfiguration and responds with HTTP 500 plus a JSON error body. It is thrown because every per-channel endpoint (list-one, fetch-block, remove) requires a channel ID in the URL path.","triggerScenarios":"Calling any per-channel endpoint of the channelparticipation API where the mux route variable 'channelID' is absent from the request path — e.g. GET/DELETE requests that hit the {channelIDKey} route but the URL lacks the channel segment, or a handler invoked outside the registered route pattern.","commonSituations":"Proxies or reverse proxies stripping path segments; constructing the endpoint URL manually and omitting the channel ID (e.g. DELETE /participation/channels instead of /participation/channels/mychannel); a client SDK generating wrong routes; custom middleware rewriting the request URL before it reaches the handler.","solutions":["Include the channel ID in the request path, e.g. DELETE /participation/channels/<channelID>","Verify the request is hitting the mux route registered with {channelIDKey}, not the list-all route","Check any proxy/middleware is not stripping or rewriting path variables","Retry the operation; a 500 here is routing-shaped, not data-shaped"],"exampleFix":"// before\ndel /participation/channels\n// after\ndel /participation/channels/mychannel","handlingStrategy":"validation","validationCode":"const m = '/participation/channels/mychannel'.match(/^\\/participation\\/channels\\/([^/]+)/);\nif (!m || !m[1]) throw new Error('channel ID required in path');","typeGuard":"function hasChannelID(vars: Record<string, string>): vars is Record<string, string> & { channelID: string } {\n  return typeof vars.channelID === 'string' && vars.channelID.length > 0;\n}","tryCatchPattern":"try {\n  const res = await fetch(`${base}/participation/channels/${cid}`, { method: 'DELETE' });\n  const body = await res.json();\n  if (!res.ok) throw new Error(body.error ?? `HTTP ${res.status}`);\n} catch (e) {\n  if (String(e).includes('missing channel ID')) console.error('URL must include channel ID');\n  else throw e;\n}","preventionTips":["Always build per-channel URLs from a template that includes {channelID}","Validate channel names against configtx rules (lowercase alphanumerics, dots/dashes, max length) before sending","Never strip trailing path segments in proxies for /participation/channels routes"],"tags":["http","rest-api","hyperledger-fabric","orderer","routing"],"backgroundTag":"missing-path-parameter","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"}