{"record":{"id":"35c4dbabaca96fb4","repo":"hyperledger/fabric","slug":"invalid-request-method-s-35c4db","errorCode":null,"errorMessage":"invalid request method: %s","messagePattern":"invalid request method: (.+?)","errorType":"http","errorClass":null,"httpStatus":405,"severity":"warning","filePath":"orderer/common/channelparticipation/restapi.go","lineNumber":632,"sourceCode":"\tswitch err {\n\tcase types.ErrSystemChannelExists:\n\t\th.sendResponseNotAllowed(resp, errors.WithMessage(err, \"cannot remove\"), http.MethodGet)\n\tcase types.ErrChannelNotExist:\n\t\th.sendResponseJsonError(resp, http.StatusNotFound, errors.WithMessage(err, \"cannot remove\"))\n\tcase types.ErrChannelPendingRemoval:\n\t\th.sendResponseJsonError(resp, http.StatusConflict, errors.WithMessage(err, \"cannot remove\"))\n\tdefault:\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.WithMessage(err, \"cannot remove\"))\n\t}\n}\n\nfunc (h *HTTPHandler) serveBadContentType(resp http.ResponseWriter, req *http.Request) {\n\terr := errors.Errorf(\"unsupported Content-Type: %s\", req.Header.Values(\"Content-Type\"))\n\th.sendResponseJsonError(resp, http.StatusBadRequest, err)\n}\n\nfunc (h *HTTPHandler) serveNotAllowed(resp http.ResponseWriter, req *http.Request) {\n\terr := errors.Errorf(\"invalid request method: %s\", req.Method)\n\n\tif _, ok := mux.Vars(req)[blockIDKey]; ok {\n\t\th.sendResponseNotAllowed(resp, err, http.MethodGet)\n\t\treturn\n\t}\n\n\tif _, ok := mux.Vars(req)[channelIDKey]; ok {\n\t\th.sendResponseNotAllowed(resp, err, http.MethodGet, http.MethodDelete)\n\t\treturn\n\t}\n\n\th.sendResponseNotAllowed(resp, err, http.MethodGet, http.MethodPost, http.MethodPut)\n}\n\nfunc negotiateContentType(req *http.Request) (string, error) {\n\tacceptReq := req.Header.Get(\"Accept\")\n\tif len(acceptReq) == 0 {\n\t\treturn \"application/json\", nil","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/channelparticipation/restapi.go#L614-L650","documentation":"serveNotAllowed is the catch-all for requests whose HTTP method is not registered for the matched route. It returns 405 (via sendResponseNotAllowed) with an 'invalid request method: %s' message, and if the route includes a block ID it advertises GET as the only allowed method. It exists to give clients a precise, machine-readable rejection when they use the wrong verb.","triggerScenarios":"Sending an unsupported method to any channelparticipation endpoint, e.g. POST to /participation/channels/{id} (only GET/DELETE allowed), DELETE on the fetch-block route, or PUT on list endpoints.","commonSituations":"Swapping PUT/POST for join vs update; using DELETE where the API expects GET for listing; generic API clients auto-negotiating methods; scripts copied from older Fabric versions where the method mapping differed.","solutions":["Use the correct verb: POST to join, PUT to update (with channel ID in path), GET to list/fetch, DELETE to remove","Read the Allow header on the 405 response — it lists the accepted methods for the route","If a block ID is in the path, use GET (fetch-block only supports GET)","Update scripts/tools that target the older pre-2.5 channel creation APIs"],"exampleFix":"// before\nDELETE /participation/channels/mychannel/blocks/latest\n// after\nGET /participation/channels/mychannel/blocks/latest","handlingStrategy":"validation","validationCode":"const allowed = { 'POST': '/participation/channels', 'PUT': `/participation/channels/${cid}`, 'GET': `/participation/channels/${cid}/blocks/${b}`, 'DELETE': `/participation/channels/${cid}` };\nconst expected = Object.entries(allowed).find(([, u]) => url.endsWith(u.replace(cid, cid)));\nif (method !== 'GET' && url.includes('/blocks/')) throw new Error('fetch-block only supports GET');","typeGuard":"type ChannelMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\nfunction isAllowedMethod(m: string, hasBlockID: boolean): m is ChannelMethod {\n  return hasBlockID ? m === 'GET' : ['GET', 'POST', 'PUT', 'DELETE'].includes(m);\n}","tryCatchPattern":"try {\n  const res = await fetch(url, { method });\n  if (res.status === 405) {\n    const allow = res.headers.get('Allow');\n    throw new Error(`Use one of: ${allow}`);\n  }\n} catch (e) {\n  if (String(e).startsWith('Use one of:')) console.error('Wrong HTTP method for this route:', e.message);\n  else throw e;\n}","preventionTips":["Map endpoints to verbs: join=POST, update=PUT, list/fetch=GET, remove=DELETE","Honor the Allow header returned with 405 responses","fetch-block endpoints accept GET only","Update legacy scripts that used the pre-2.5 channel APIs"],"tags":["http","method-not-allowed","rest-api","hyperledger-fabric","orderer"],"backgroundTag":"method-not-allowed","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"}