GopeedLab/gopeed · error

method not allowed

Error message

method not allowed

What it means

The registry serves blobs from a loopback HTTP server (paths under /__blob/) and its handler is read-only: only GET is accepted. ServeHTTP rejects every other method with 405 and an 'Allow: GET' response header before it even parses the blob ID, so the rejection is independent of whether the blob exists. Any HEAD, POST, PUT, DELETE, or OPTIONS request to a blob URL gets this response.

Source

Thrown at internal/blob/registry.go:311

	if err != nil {
		return "", err
	}
	r.listener = listener
	r.baseURL = "http://" + listener.Addr().String() + urlPathPrefix
	server := &http.Server{Handler: r}
	r.server = server
	go func() {
		if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
			_ = r.Close()
		}
	}()
	return r.baseURL, nil
}

func (r *Registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodGet {
		w.Header().Set("Allow", "GET")
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
	id := parseRequest(req)
	if id == "" {
		http.NotFound(w, req)
		return
	}
	src, err := r.getByID(id)
	if err != nil {
		http.NotFound(w, req)
		return
	}
	meta, open, session := src.acquireOpen()
	if open == nil {
		http.NotFound(w, req)
		return
	}
	if session != nil {

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Change the request to GET — it is the only method the endpoint serves
  2. From Go, skip HTTP probing entirely: use Registry.IsURL(url) or Registry.Metadata(url) to check a blob
  3. If you mount the registry behind your own mux, route only GET to /__blob/ and answer other methods yourself
  4. When debugging, read the 'Allow: GET' response header to confirm the supported method set

Example fix

// before
resp, err := http.Head(blobURL) // 405 method not allowed

// after
resp, err := http.Get(blobURL)
// existence checks from Go code:
if reg.IsURL(blobURL) { /* source registered */ }
Defensive patterns

Strategy: validation

Validate before calling

// blob URLs are GET-only: gate the method before issuing the request
if method != http.MethodGet {
	return fmt.Errorf("blob endpoint is GET-only, got %s", method)
}
req, err := http.NewRequest(http.MethodGet, blobURL, nil)
if err != nil {
	return err
}

Try / catch

resp, err := client.Get(blobURL)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusMethodNotAllowed { // 405
	// wrong method; only GET is served (resp.Header.Get("Allow") == "GET")
	return fmt.Errorf("blob endpoint rejected %s; use GET", method)
}

Prevention

When it happens

Trigger: Issuing any non-GET request to a URL returned by Registry.CreateBlob/CreateOpener, e.g. http.Head(blobURL) for an existence probe, or a generic HTTP client whose default method is POST. The method check at registry.go:309 runs first, so even HEAD on a valid, live blob URL returns 405.

Common situations: Health/existence checks written with http.Head; download frameworks that probe servers with OPTIONS or HEAD before GETting; axios/fetch calls that default to POST; wrapping code that forwards all methods to the registry handler unchanged.

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/8ab6428682f63529. Report an issue: GitHub.