{"record":{"id":"6c01de09932625d1","repo":"router-for-me/CLIProxyAPI","slug":"auth-provider-start-login-panic-v","errorCode":null,"errorMessage":"auth provider start login panic: %v","messagePattern":"auth provider start login panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/auth_provider.go","lineNumber":276,"sourceCode":"func (h *Host) StartLogin(ctx context.Context, provider string, baseURL string) (pluginapi.AuthLoginStartResponse, bool, error) {\n\trecord := h.authProviderRecord(provider)\n\tif record == nil {\n\t\treturn pluginapi.AuthLoginStartResponse{}, false, nil\n\t}\n\treturn h.callStartLogin(ctx, *record, provider, baseURL)\n}\n\nfunc (h *Host) callStartLogin(ctx context.Context, record capabilityRecord, provider string, baseURL string) (resp pluginapi.AuthLoginStartResponse, handled bool, err error) {\n\tauthProvider := record.plugin.Capabilities.AuthProvider\n\tif h == nil || authProvider == nil || h.isPluginFused(record.id) || !h.recordCurrent(record) {\n\t\treturn pluginapi.AuthLoginStartResponse{}, false, nil\n\t}\n\tdefer func() {\n\t\tif recovered := recover(); recovered != nil {\n\t\t\th.fusePlugin(record.id, \"AuthProvider.StartLogin\", recovered)\n\t\t\tresp = pluginapi.AuthLoginStartResponse{}\n\t\t\thandled = false\n\t\t\terr = fmt.Errorf(\"auth provider start login panic: %v\", recovered)\n\t\t}\n\t}()\n\treq := pluginapi.AuthLoginStartRequest{\n\t\tProvider:   normalizeProviderID(provider),\n\t\tBaseURL:    strings.TrimSpace(baseURL),\n\t\tHost:       h.hostConfigSummary(),\n\t\tHTTPClient: h.newHTTPClient(nil),\n\t}\n\tresp, errStart := authProvider.StartLogin(ctx, req)\n\tif errStart != nil {\n\t\treturn pluginapi.AuthLoginStartResponse{}, true, errStart\n\t}\n\treturn resp, true, nil\n}\n\nfunc (h *Host) PollLogin(ctx context.Context, provider, state string, metadata ...map[string]any) (pluginapi.AuthLoginPollResponse, bool, error) {\n\trecord := h.authProviderRecord(provider)\n\tif record == nil {","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/auth_provider.go#L258-L294","documentation":"This error is produced by the plugin host's panic guard around a plugin's AuthProvider.StartLogin capability. When a loaded plugin's StartLogin method panics (instead of returning an error), the host recovers the panic, 'fuses' the plugin (marks it as failed after repeated panics), and returns this error to the caller. It means the crash happened inside third-party plugin code, not in the host itself.","triggerScenarios":"Calling the login-start flow for a provider backed by a plugin (e.g. a management/API endpoint that triggers login) whose StartLogin implementation panics: nil map writes, nil pointer dereferences, index out of range, or missing metadata the plugin expected in the AuthLoginStartRequest (Provider, BaseURL, Host, HTTPClient).","commonSituations":"Installing a buggy or version-mismatched auth plugin; plugin assumes fields the host left empty; plugin written against an older pluginapi AuthLoginStartRequest shape; plugin panics when BaseURL is empty because the caller passed no --base-url.","solutions":["Check the plugin identity in the fused-plugin log line (fusePlugin records the capability 'AuthProvider.StartLogin' and the recovered value) to identify which plugin panicked.","Reproduce outside the host: run the plugin's StartLogin directly with the same Provider/BaseURL inputs to get a real stack trace.","Update or replace the plugin with a version that returns errors instead of panicking; report the panic plus recovered value to the plugin author.","If you own the plugin, audit StartLogin for nil map dereferences, unguarded type assertions, and missing-value assumptions; add defensive checks and return errors.","Restart or reload the host to reset the fuse if the panic was a one-off (e.g. transient nil HTTPClient)."],"exampleFix":"// plugin side, before (panics on nil metadata)\nfunc (p *Provider) StartLogin(ctx context.Context, req pluginapi.AuthLoginStartRequest) (pluginapi.AuthLoginStartResponse, error) {\n\turl := req.Metadata[\"redirect\"] // panics if Metadata is nil\n\t...\n}\n\n// after\nfunc (p *Provider) StartLogin(ctx context.Context, req pluginapi.AuthLoginStartRequest) (pluginapi.AuthLoginStartResponse, error) {\n\tif req.Metadata == nil {\n\t\treturn pluginapi.AuthLoginStartResponse{}, fmt.Errorf(\"start login: metadata is required\")\n\t}\n\turl, ok := req.Metadata[\"redirect\"]\n\tif !ok {\n\t\treturn pluginapi.AuthLoginStartResponse{}, fmt.Errorf(\"start login: redirect missing\")\n\t}\n\t...\n}","handlingStrategy":"try-catch","validationCode":"// Before starting login, confirm the provider is plugin-backed and not fused\nif !host.ProviderHasActiveAuthPlugin(providerID) {\n    return fmt.Errorf(\"provider %s has no healthy auth plugin; run built-in flow\", providerID)\n}","typeGuard":"func isHealthyAuthProvider(h *pluginhost.Host, provider string) bool {\n    if h == nil {\n        return false\n    }\n    rec := h.AuthProviderRecord(provider)\n    return rec != nil && rec.Plugin.Capabilities.AuthProvider != nil && !h.IsPluginFused(rec.ID)\n}","tryCatchPattern":"resp, handled, err := host.CallStartLogin(ctx, provider, baseURL)\nif err != nil {\n    if strings.Contains(err.Error(), \"start login panic\") {\n        log.WithError(err).Error(\"auth plugin panicked during start login; falling back to built-in flow\")\n        // plugin is now fused; use non-plugin path or surface actionable error to user\n    }\n    return err\n}\nif !handled {\n    // no plugin claimed this provider; use built-in auth flow\n}","preventionTips":["Pin plugin versions tested against your host version.","Prefer plugins that return errors instead of panicking; check their changelog for panic fixes.","Watch fuse logs so a repeatedly panicking plugin is removed before it degrades login flows."],"tags":["plugin","panic","auth","oauth","pluginhost"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}