{"record":{"id":"4c7a211adf81c74f","repo":"juicedata/juicefs","slug":"post-required","errorCode":null,"errorMessage":"POST required","messagePattern":"POST required","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"pkg/sync/cluster.go","lineNumber":287,"sourceCode":"\t\t\t\tbase := withoutSize(o)\n\t\t\t\tif base.Size() > multipartCheckpointThreshold && (nsize == base.Size() || nsize == markChecksum) {\n\t\t\t\t\tif cp := checkpointMgr.GetMultipartCheckpoint(base.Key(), base.Size(), base.Mtime()); cp != nil {\n\t\t\t\t\t\tobjs[i] = withMultipart(o, cp)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\td, err := marshalObjects(objs)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tlogger.Debugf(\"send %d objects(%s) to %s\", len(objs), humanize.IBytes(uint64(total)), req.RemoteAddr)\n\t\t_, _ = w.Write(d)\n\t})\n\tmux.HandleFunc(\"/stats\", func(w http.ResponseWriter, req *http.Request) {\n\t\tif req.Method != \"POST\" {\n\t\t\thttp.Error(w, \"POST required\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\td, err := io.ReadAll(req.Body)\n\t\tif err != nil {\n\t\t\tlogger.Errorf(\"read: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tvar r Stat\n\t\terr = json.Unmarshal(d, &r)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tupdateStats(&r)\n\t\tsrcDelayDelMu.Lock()\n\t\tsrcDelayDel = append(srcDelayDel, r.DelayDelDir...)\n\t\tsrcDelayDelMu.Unlock()\n\t\tif checkpointMgr != nil {","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/sync/cluster.go#L269-L305","documentation":"The sync cluster worker HTTP endpoint (/stats) in pkg/sync/cluster.go only accepts POST requests and replies 'POST required' with status 400 for any other method. This endpoint receives statistics pushed by peer workers during distributed juicefs sync jobs.","triggerScenarios":"Issuing a GET (or PUT/DELETE) request to http://<worker>:<port>/stats on a sync worker's HTTP port; health-check probes configured with GET against this endpoint.","commonSituations":"Operators probing worker liveness with a browser or curl (defaults to GET); monitoring systems doing GET-based health checks against the sync cluster port.","solutions":["Use POST when querying the /stats endpoint: curl -X POST http://host:port/stats","Reconfigure health checks to use POST, or point them at a different endpoint that accepts GET","If using curl, add -X POST or --data '' to force the POST method"],"exampleFix":"// before\ncurl http://127.0.0.1:8999/stats\n// after\ncurl -X POST http://127.0.0.1:8999/stats","handlingStrategy":"validation","validationCode":"req, _ := http.NewRequest(http.MethodPost, baseURL+\"/stats\", nil)\n// ensure req.Method == http.MethodPost before Do()","typeGuard":"func isPost(r *http.Request) bool { return r.Method == http.MethodPost }","tryCatchPattern":"resp, err := client.Do(req)\nif err != nil { return err }\nif resp.StatusCode == http.StatusBadRequest { /* wrong method: retry as POST */ }","preventionTips":["Always use POST for sync worker /stats endpoints","Do not point GET-based health checks at the sync worker HTTP port","Read the handler registration (mux.HandleFunc(\"/stats\"...)) to see accepted methods"],"tags":["http","sync","method-not-allowed"],"backgroundTag":"http-error-response","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}