{"record":{"id":"a21738fbe4c5b964","repo":"router-for-me/CLIProxyAPI","slug":"plugin-executor-s-refresh-panic-v","errorCode":null,"errorMessage":"plugin executor %s refresh panic: %v","messagePattern":"plugin executor (.+?) refresh panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/adapters_executors.go","lineNumber":700,"sourceCode":"\treturn &coreexecutor.StreamResult{\n\t\tHeaders: cloneHeader(pluginResp.Headers),\n\t\tChunks:  mapExecutorStreamChunks(ctx, a.translateExecutorStreamChunks(ctx, prepared, pluginResp.Chunks)),\n\t}, nil\n}\n\nfunc (a *executorAdapter) Refresh(ctx context.Context, auth *coreauth.Auth) (refreshed *coreauth.Auth, err error) {\n\tif a == nil || a.executor == nil || a.host.isPluginFused(a.pluginID) || !a.host.pluginIdentityCurrent(a.pluginID, a.path, a.version) {\n\t\treturn nil, fmt.Errorf(\"plugin executor %s is unavailable\", a.Identifier())\n\t}\n\trecord := a.host.authProviderRecord(authProvider(auth))\n\tif record == nil || record.plugin.Capabilities.AuthProvider == nil {\n\t\treturn auth.Clone(), nil\n\t}\n\tdefer func() {\n\t\tif recovered := recover(); recovered != nil {\n\t\t\ta.host.fusePlugin(record.id, \"AuthProvider.RefreshAuth\", recovered)\n\t\t\trefreshed = nil\n\t\t\terr = fmt.Errorf(\"plugin executor %s refresh panic: %v\", a.Identifier(), recovered)\n\t\t}\n\t}()\n\n\tpluginResp, errRefresh := record.plugin.Capabilities.AuthProvider.RefreshAuth(ctx, pluginapi.AuthRefreshRequest{\n\t\tAuthID:       authID(auth),\n\t\tAuthProvider: authProvider(auth),\n\t\tStorageJSON:  storageJSONFromAuth(auth),\n\t\tMetadata:     cloneAnyMap(authMetadata(auth)),\n\t\tAttributes:   authAttributes(auth),\n\t\tHost:         a.host.hostConfigSummary(),\n\t\tHTTPClient:   a.host.newHTTPClient(auth),\n\t})\n\tif errRefresh != nil {\n\t\treturn nil, errRefresh\n\t}\n\tdata := pluginResp.Auth\n\tif strings.TrimSpace(data.Provider) == \"\" {\n\t\tdata.Provider = authProvider(auth)","sourceCodeStart":682,"sourceCodeEnd":718,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/adapters_executors.go#L682-L718","documentation":"The plugin's AuthProvider.RefreshAuth capability panicked and the adapter's deferred recover() converted it into an error. The host also fuses the plugin (record.id) so later calls fail fast, and the refresh returns no credentials.","triggerScenarios":"Calling executorAdapter.Refresh when the plugin exposes an AuthProvider capability and its RefreshAuth function panics — e.g. malformed StorageJSON, expired token shape it does not expect, or a nil map/slice dereference in the plugin's refresh logic.","commonSituations":"Custom OAuth provider plugin with a buggy refresh handler; token storage schema drift between plugin versions; auth file manually edited so Metadata/StorageJSON no longer parses into the plugin's expected struct.","solutions":["Inspect the recovered panic value (%v) in host logs and the AuthID/AuthProvider being refreshed","Fix the plugin's RefreshAuth to handle the exact StorageJSON/Metadata it receives (validate before dereferencing)","Reinstall/reload the fixed plugin to clear the fuse and retry refresh","As a workaround, re-run the OAuth login flow for that provider so fresh auth data replaces the input that triggers the panic"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"refreshed, err := adapter.Refresh(ctx, auth)\nif err != nil {\n    if strings.Contains(err.Error(), \"refresh panic\") {\n        // plugin fused; fall back to re-auth or another credential\n        return reauthenticate(auth)\n    }\n    return nil, err\n}","preventionTips":["Fuzz plugin refresh handlers with empty/expired/malformed StorageJSON before shipping","Never hand-edit auth JSON files that plugins consume","Keep an unauthenticated-retry or re-login path for each plugin provider"],"tags":["plugin","panic","auth-refresh","circuit-breaker","oauth"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}