sipeed/picoclaw · error

device code request failed: %s

Error message

device code request failed: %s

What it means

The device-code endpoint answered a non-200 status (oauth.go:271); the raw body is embedded in the error string. Unlike 357 (no response at all), here the server spoke and refused: 404 when cfg.Issuer does not host the /api/accounts/deviceauth/usercode path, 400/422 for an unknown/invalid client_id, 429 for rate limiting, 5xx for upstream faults.

Source

Thrown at pkg/auth/oauth.go:271

		"client_id": cfg.ClientID,
	})

	resp, err := http.Post(
		cfg.Issuer+"/api/accounts/deviceauth/usercode",
		"application/json",
		strings.NewReader(string(reqBody)),
	)
	if err != nil {
		return nil, fmt.Errorf("requesting device code: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading device code response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("device code request failed: %s", string(body))
	}

	deviceResp, err := parseDeviceCodeResponse(body)
	if err != nil {
		return nil, fmt.Errorf("parsing device code response: %w", err)
	}

	if deviceResp.Interval < 1 {
		deviceResp.Interval = 5
	}

	return &DeviceCodeInfo{
		DeviceAuthID: deviceResp.DeviceAuthID,
		UserCode:     deviceResp.UserCode,
		VerifyURL:    cfg.Issuer + "/codex/device",
		Interval:     deviceResp.Interval,
	}, nil
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Read the embedded body — it states the server's reason verbatim
  2. 404: fix cfg.Issuer to the host that actually serves /api/accounts/deviceauth/usercode
  3. 4xx client errors: verify cfg.ClientID belongs to this issuer environment
  4. 429/5xx: back off and retry later; do not loop tightly

Example fix

# before: issuer/path mismatch returns 404
cfg.Issuer = "https://oauth2.googleapis.com"

# after: issuer that implements the picoclaw device-auth endpoint
cfg.Issuer = "https://accounts.pico.ltd"
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := auth.RequestDeviceCode(cfg); err != nil {
    if strings.Contains(err.Error(), "device code request failed") {
        body := err.Error()
        switch {
        case strings.Contains(body, "404"): fixIssuer(cfg) // wrong issuer/path
        case strings.Contains(body, "400"), strings.Contains(body, "422"): fixClientID(cfg)
        case strings.Contains(body, "429"): return retryWithBackoff
        default: return err
        }
    }
    return err
}

Prevention

When it happens

Trigger: cfg.Issuer pointed at a standard OAuth issuer whose device path differs (404 HTML body); cfg.ClientID from a different environment (invalid client); hammering the endpoint on every poll loop instead of honoring deviceResp.Interval; provider incidents.

Common situations: Copy-paste of Google/Anthropic issuer values into a picoclaw device flow; staging client_id used against production; polling loops that call RequestDeviceCode repeatedly instead of the token-poll endpoint.

Related errors


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