router-for-me/CLIProxyAPI · error
plugin auth provider refresh is unavailable for provider %s
Error message
plugin auth provider refresh is unavailable for provider %s
What it means
The plugin-based auth refresh compatibility executor cannot refresh a credential: neither the home provider nor the plugin host handled the refresh, yet the auth carries a refresh_token in its Metadata. internal/pluginhost/plugin_refresh_compat_executor.go then returns this error naming the provider instead of silently returning a stale credential, so callers know re-login is required.
Source
Thrown at internal/pluginhost/plugin_refresh_compat_executor.go:135
return nil, fmt.Errorf("plugin refresh compat executor is unavailable")
}
if ctx == nil {
ctx = context.Background()
}
if refreshed, handled, errHome := helps.RefreshAuthViaHome(ctx, e.cfg, auth); handled {
return refreshed, errHome
}
if e.host != nil {
if refreshed, handled, errRefresh := e.host.RefreshAuth(ctx, auth); handled {
return refreshed, errRefresh
}
}
if authHasRefreshToken(auth) {
provider := e.Identifier()
if provider == "" && auth != nil {
provider = strings.TrimSpace(auth.Provider)
}
return nil, fmt.Errorf("plugin auth provider refresh is unavailable for provider %s", provider)
}
if auth == nil {
return nil, nil
}
return auth.Clone(), nil
}
func authHasRefreshToken(auth *coreauth.Auth) bool {
if auth == nil || auth.Metadata == nil {
return false
}
if token, _ := auth.Metadata["refresh_token"].(string); strings.TrimSpace(token) != "" {
return true
}
if token, _ := auth.Metadata["refreshToken"].(string); strings.TrimSpace(token) != "" {
return true
}
return falseView on GitHub (pinned to 78f0c4079e)
Solutions
- Re-run the OAuth/login flow for that provider to mint fresh credentials (the refresh token has no usable path here)
- Install/enable the plugin that owns the provider so host.RefreshAuth can handle it
- Remove the stale auth file from auths/ if the provider is no longer used
- Verify the provider identifier in the error matches an executor/plugin you actually ship
Defensive patterns
Strategy: fallback
Validate before calling
if authHasRefreshToken(auth) {
if _, handled, _ := host.RefreshAuth(ctx, auth); !handled {
// no plugin can refresh this: route the user to re-login now
}
} Try / catch
refreshed, err := executor.RefreshAuth(ctx, auth)
if err != nil && strings.Contains(err.Error(), "refresh is unavailable") {
log.Warnf("credential for %s cannot be refreshed; starting interactive login", auth.Provider)
return beginLoginFlow(ctx, auth.Provider)
} Prevention
- Install and enable the plugin that issued the credential before its access token expires
- Proactively refresh tokens on a schedule instead of waiting for expiry
- Delete auth files for providers whose plugins you have removed
When it happens
Trigger: RefreshAuth is invoked on an Auth whose Metadata['refresh_token'] is non-empty, the plugin backing that provider is not loaded/disabled/does not implement refresh, and the built-in executor registry has no handler for it either.
Common situations: Plugin binary missing or removed after the credential was issued; plugin version that lacks a refresh implementation; provider disabled in config but old auth file still present in auths/; auth imported from another installation whose provider plugin is not installed.
Related errors
- auth provider start login panic: %v
- auth provider poll login panic: %v
- refresh response did not include access_token
- kimi: refresh token is required
- kimi: failed to create refresh request: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/2d4207fca80fba6f.
Report an issue: GitHub.