router-for-me/CLIProxyAPI · error

auth provider refresh panic: %v

Error message

auth provider refresh panic: %v

What it means

This error comes from the panic guard around a plugin's AuthProvider.RefreshAuth capability. When a plugin's token-refresh handler panics, the host recovers it, fuses the plugin, and returns this error with handled=true, meaning the refresh was claimed by the plugin but failed. The existing auth is left untouched and will be retried or expired according to normal policy.

Source

Thrown at internal/pluginhost/auth_provider.go:347

}

func (h *Host) RefreshAuth(ctx context.Context, auth *coreauth.Auth) (refreshed *coreauth.Auth, handled bool, err error) {
	if h == nil || auth == nil {
		return nil, false, nil
	}
	record := h.authProviderRecord(authProvider(auth))
	if record == nil || record.plugin.Capabilities.AuthProvider == nil {
		return nil, false, nil
	}
	if !h.recordCurrent(*record) {
		return nil, false, nil
	}
	defer func() {
		if recovered := recover(); recovered != nil {
			h.fusePlugin(record.id, "AuthProvider.RefreshAuth", recovered)
			refreshed = nil
			handled = true
			err = fmt.Errorf("auth provider refresh panic: %v", recovered)
		}
	}()

	pluginResp, errRefresh := record.plugin.Capabilities.AuthProvider.RefreshAuth(ctx, pluginapi.AuthRefreshRequest{
		AuthID:       authID(auth),
		AuthProvider: authProvider(auth),
		StorageJSON:  storageJSONFromAuth(auth),
		Metadata:     cloneAnyMap(authMetadata(auth)),
		Attributes:   authAttributes(auth),
		Host:         h.hostConfigSummary(),
		HTTPClient:   h.newHTTPClient(auth),
	})
	if errRefresh != nil {
		return nil, true, errRefresh
	}
	data := pluginResp.Auth
	if strings.TrimSpace(data.Provider) == "" {
		data.Provider = authProvider(auth)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Read the fusePlugin log for 'AuthProvider.RefreshAuth' plus the recovered value to identify the plugin and panic cause.
  2. Check the stored auth file (auth dir) for the affected provider: malformed or version-skewed StorageJSON is the most common trigger.
  3. Re-authenticate the provider (delete the stale auth entry and redo login) so the plugin receives fresh, well-formed StorageJSON.
  4. Fix the plugin to validate StorageJSON (json.Unmarshal errors returned, not panicked) and nil-check Metadata/Attributes.
  5. Update the plugin to a version compatible with the current pluginapi.AuthRefreshRequest shape.

Example fix

// plugin side, before
func (p *Provider) RefreshAuth(ctx context.Context, req pluginapi.AuthRefreshRequest) (pluginapi.AuthRefreshResponse, error) {
	var st stored
	json.Unmarshal([]byte(req.StorageJSON), &st) // error ignored
	return pluginapi.AuthRefreshResponse{AccessToken: st.Token.Value}, nil // panics if unmarshal failed
}

// after
func (p *Provider) RefreshAuth(ctx context.Context, req pluginapi.AuthRefreshRequest) (pluginapi.AuthRefreshResponse, error) {
	var st stored
	if err := json.Unmarshal([]byte(req.StorageJSON), &st); err != nil {
		return pluginapi.AuthRefreshResponse{}, fmt.Errorf("refresh: decode storage: %w", err)
	}
	if st.Token.Value == "" {
		return pluginapi.AuthRefreshResponse{}, fmt.Errorf("refresh: stored token is empty")
	}
	return pluginapi.AuthRefreshResponse{AccessToken: st.Token.Value}, nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Skip plugin refresh for providers whose auth storage is empty/malformed
if strings.TrimSpace(auth.StorageJSON()) == "" {
    return nil, fmt.Errorf("auth %s has no storage blob; re-login required", auth.ID)
}

Type guard

func refreshSafe(h *pluginhost.Host, auth coreauth.Auth) bool {
    rec := h.AuthProviderRecord(auth.Provider())
    return rec != nil && !h.IsPluginFused(rec.ID) && strings.TrimSpace(auth.StorageJSON()) != ""
}

Try / catch

refreshed, handled, err := host.CallRefreshAuth(ctx, auth)
if err != nil {
    if strings.Contains(err.Error(), "refresh panic") {
        // handled=true: plugin claimed refresh but crashed; keep old auth and schedule re-login
        log.WithError(err).Warn("plugin refresh panicked; retaining previous auth")
        return auth, nil
    }
    return nil, err
}

Prevention

When it happens

Trigger: A background or on-demand token refresh for a provider served by a plugin whose RefreshAuth panics: invalid StorageJSON the plugin fails to parse defensively, unexpected nil Attributes/Metadata, or type assertion failures on cloned maps.

Common situations: A plugin whose stored credential format changed between versions (StorageJSON from an older auth file); auth files on disk written by a different plugin version; plugins that assume Metadata keys exist and dereference without checks.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/52349aadee79fbf8. Report an issue: GitHub.