charmbracelet/crush · error
failed to refresh OAuth token: status code %d
Error message
failed to refresh OAuth token: status code %d
What it means
RefreshOAuthToken expects HTTP 200 from /workspaces/{id}/config/refresh-oauth. Any other status (4xx/5xx) produces this error. It means the server received the request but refused or failed it — unlike error 270, the network round-trip succeeded.
Source
Thrown at internal/client/config.go:160
if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
return nil, false, fmt.Errorf("failed to decode import copilot response: %w", err)
}
return result.Token, result.Success, nil
}
// RefreshOAuthToken refreshes an OAuth token for a provider on the
// server.
func (c *Client) RefreshOAuthToken(ctx context.Context, id string, scope config.Scope, providerID string) error {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/config/refresh-oauth", id), nil, jsonBody(struct {
Scope config.Scope `json:"scope"`
ProviderID string `json:"provider_id"`
}{Scope: scope, ProviderID: providerID}), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return fmt.Errorf("failed to refresh OAuth token: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to refresh OAuth token: status code %d", rsp.StatusCode)
}
return nil
}
// ProjectNeedsInitialization checks if the project needs
// initialization.
func (c *Client) ProjectNeedsInitialization(ctx context.Context, id string) (bool, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/project/needs-init", id), nil, nil)
if err != nil {
return false, fmt.Errorf("failed to check project init: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return false, fmt.Errorf("failed to check project init: status code %d", rsp.StatusCode)
}
var result struct {
NeedsInit bool `json:"needs_init"`
}View on GitHub (pinned to 7944b8e522)
Solutions
- Log rsp.StatusCode variants: re-authenticate the provider interactively if 401/403 (refresh grant is dead)
- Verify the workspace id and providerID are correct if you see 404
- Check server logs for 5xx responses; this is a server-side token-exchange failure
- Retry with backoff only for 429/5xx; do not retry 4xx client errors
Example fix
// before
if err := client.RefreshOAuthToken(ctx, id, scope, providerID); err != nil {
return err
}
// after
if err := client.RefreshOAuthToken(ctx, id, scope, providerID); err != nil {
if strings.Contains(err.Error(), "status code 401") {
return requireReauthentication(providerID)
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the provider is registered in the workspace before refreshing
providers, err := client.ListProviders(ctx, id)
if err != nil {
return err
}
found := false
for _, p := range providers {
if p.ID == providerID {
found = true
}
}
if !found {
return fmt.Errorf("provider %q not in workspace %s", providerID, id)
} Type guard
func isStatusError(err error, code int) bool {
return err != nil && strings.Contains(err.Error(), fmt.Sprintf("status code %d", code))
} Try / catch
if err := c.RefreshOAuthToken(ctx, id, scope, pid); err != nil {
if isStatusError(err, 401) || isStatusError(err, 403) {
return triggerInteractiveLogin(pid)
}
return err
} Prevention
- Distinguish 4xx (do not retry) from 429/5xx (retry with backoff)
- Re-authenticate providers proactively before refresh grants expire
- Validate workspace and provider ids before calling refresh
- Log response statuses server-side to diagnose refresh failures
When it happens
Trigger: Calling RefreshOAuthToken and the server responds with a non-200 status, e.g. 401 when the refresh grant itself is invalid, 404 for an unknown workspace id, or 500 on a server-side provider token exchange failure.
Common situations: The stored refresh token was revoked or expired beyond the grace window; the providerID no longer exists in the workspace config; the caller lacks scope permission for the workspace; server bug during provider token rotation.
Related errors
- failed to refresh OAuth token: %w
- failed to check project init: status code %d
- failed to mark project initialized: status code %d
- failed to get init prompt: status code %d
- failed to list skills: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/e9c91fcba6342013.
Report an issue: GitHub.