sipeed/picoclaw · error

failed to read request body

Error message

failed to read request body

What it means

Returned by POST /api/oauth/login (handleOAuthLogin) when io.ReadAll of the request body fails before parsing. The read is capped at 1 MiB; failure is transport-level (connection aborted mid-body, truncated chunked encoding), not payload content — bad JSON would produce 'invalid JSON: %v' instead.

Source

Thrown at web/backend/api/oauth.go:176

				item.Status = "needs_refresh"
			default:
				item.Status = "connected"
			}
		}

		providersResp = append(providersResp, item)
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"providers": providersResp,
	})
}

func (h *Handler) handleOAuthLogin(w http.ResponseWriter, r *http.Request) {
	body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		http.Error(w, "failed to read request body", http.StatusBadRequest)
		return
	}
	defer r.Body.Close()

	var req struct {
		Provider string `json:"provider"`
		Method   string `json:"method"`
		Token    string `json:"token"`
	}
	if err = json.Unmarshal(body, &req); err != nil {
		http.Error(w, fmt.Sprintf("invalid JSON: %v", err), http.StatusBadRequest)
		return
	}

	provider, err := normalizeOAuthProvider(req.Provider)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Retry the login POST — transient aborts dominate
  2. Prevent double-submit/abort races in the UI (disable the button while in flight)
  3. Check the reverse proxy's request-body timeout if it recurs
Defensive patterns

Strategy: validation

Validate before calling

function loginBody(provider: string, method: string, token?: string): string {
  if (!provider || !method) throw new Error('provider and method are required');
  return JSON.stringify({ provider, method, ...(token ? { token } : {}) });
}

Try / catch

try {
  const res = await fetch('/api/oauth/login', {...});
  if (res.status === 400 && (await res.text()).includes('read request body')) {
    /* aborted transfer — retry once with the same payload */
  }
} catch (e) { /* network */ }

Prevention

When it happens

Trigger: Login POST aborted by the client after headers were sent; proxy cutting the body; Content-Length mismatch on a hand-rolled HTTP client.

Common situations: Login dialog unmounted mid-submit (SPA route change); aggressive proxy timeouts; curl interrupted mid-request; browser offline the moment login is clicked.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/aa948e299761c0ff. Report an issue: GitHub.