sipeed/picoclaw · error

token is required

Error message

token is required

What it means

Returned as HTTP 400 by POST /api/oauth/login when method is "token" and the token field is empty after trimming. This is the only required field for token login — provider and method were already validated. Whitespace-only tokens are rejected because the value is strings.TrimSpace'd before the check.

Source

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

		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	method := strings.ToLower(strings.TrimSpace(req.Method))
	if !isOAuthMethodSupported(provider, method) {
		http.Error(
			w,
			fmt.Sprintf("unsupported login method %q for provider %q", method, provider),
			http.StatusBadRequest,
		)
		return
	}

	switch method {
	case oauthMethodToken:
		token := strings.TrimSpace(req.Token)
		if token == "" {
			http.Error(w, "token is required", http.StatusBadRequest)
			return
		}

		cred := &auth.AuthCredential{
			AccessToken: token,
			Provider:    provider,
			AuthMethod:  oauthMethodToken,
		}
		if err := h.persistCredentialAndConfig(provider, oauthMethodToken, cred); err != nil {
			http.Error(w, fmt.Sprintf("token login failed: %v", err), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"status":   "ok",
			"provider": provider,
			"method":   method,

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Include a non-empty, non-whitespace "token" string in the JSON body: {"provider":"anthropic","method":"token","token":"sk-ant-..."}.
  2. Trim the token client-side before submitting (token.trim()) so whitespace-only values are caught in the UI.
  3. Check that the JSON key is exactly "token", not "api_key"/"access_token".
  4. Ensure JSON.stringify does not drop the field because the value is undefined — default it or fail the submit.

Example fix

// before
await fetch('/api/oauth/login', {method:'POST', body: JSON.stringify({provider, method:'token', token: apiKey || undefined})});
// undefined is omitted by JSON.stringify -> 400 token is required

// after
const token = (apiKey ?? '').trim();
if (!token) throw new Error('Paste an API token first');
await fetch('/api/oauth/login', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({provider, method:'token', token})});
Defensive patterns

Strategy: validation

Validate before calling

function assertToken(token) {
  const t = String(token ?? '').trim();
  if (!t) throw new Error('token is required');
  return t;
}

Type guard

function hasToken(v) { return typeof v === 'string' && v.trim().length > 0; }

Prevention

When it happens

Trigger: POST /api/oauth/login with {"provider":"anthropic","method":"token","token":""} or {"token":" "} or with the field named differently (e.g. "api_key" or "accessToken") so the struct field stays zero.

Common situations: Frontend form with an empty paste field; token stored under a different key in state and serialized as token: undefined (dropped by JSON.stringify); users pasting a newline-only string; API clients built from the Anthropic docs that send an Authorization header instead of a body token.

Related errors


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