{"record":{"id":"9a6fe1923bed2e1c","repo":"router-for-me/CLIProxyAPI","slug":"plugin-executor-s-stream-panic-v","errorCode":null,"errorMessage":"plugin executor %s stream panic: %v","messagePattern":"plugin executor (.+?) stream panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/adapters_executors.go","lineNumber":670,"sourceCode":"\tif errExecute != nil {\n\t\treturn coreexecutor.Response{}, errExecute\n\t}\n\treturn coreexecutor.Response{\n\t\tPayload:  a.translateExecutorResponse(ctx, prepared, pluginResp.Payload, false, nil),\n\t\tMetadata: cloneAnyMap(pluginResp.Metadata),\n\t\tHeaders:  cloneHeader(pluginResp.Headers),\n\t}, nil\n}\n\nfunc (a *executorAdapter) ExecuteStream(ctx context.Context, auth *coreauth.Auth, req coreexecutor.Request, opts coreexecutor.Options) (result *coreexecutor.StreamResult, 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\tdefer func() {\n\t\tif recovered := recover(); recovered != nil {\n\t\t\ta.host.fusePlugin(a.pluginID, \"Executor.ExecuteStream\", recovered)\n\t\t\tresult = nil\n\t\t\terr = fmt.Errorf(\"plugin executor %s stream panic: %v\", a.Identifier(), recovered)\n\t\t}\n\t}()\n\n\tprepared, errPrepare := a.prepareExecutorCall(req, opts)\n\tif errPrepare != nil {\n\t\treturn nil, errPrepare\n\t}\n\tpluginResp, errExecuteStream := a.executor.ExecuteStream(ctx, buildExecutorRequest(a.host, a.provider, auth, prepared.req, prepared.opts))\n\tif errExecuteStream != nil {\n\t\treturn nil, errExecuteStream\n\t}\n\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) {","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/adapters_executors.go#L652-L688","documentation":"A panic inside the plugin's ExecuteStream implementation was caught by the executor adapter's deferred recover(). The host converts the panic into a normal Go error, nulls the stream result, and trips the plugin's fuse (circuit breaker) via fusePlugin, so subsequent calls to this plugin fail fast with 'plugin executor ... is unavailable' until the fuse is cleared.","triggerScenarios":"Calling ExecuteStream on an executorAdapter whose backing plugin (out-of-process or in-process plugin executor) panics during streaming execution — e.g. nil map write, index out of range, or nil pointer dereference inside the plugin's streaming code path.","commonSituations":"Third-party or custom plugin written in Go that has a bug in its stream handler; plugin built against an older plugin API version whose stream request shape changed; plugin receiving an unexpected model/payload and dereferencing a nil field.","solutions":["Check the host logs for the panic value and stack captured before the fuse tripped; reproduce the plugin panic by running the plugin standalone","Fix the panic inside the plugin's ExecuteStream implementation (the %v in the message is the recovered panic value)","Rebuild/reinstall the plugin so the host picks up the fixed binary; reload plugins or restart the proxy to clear the fuse","If the plugin is from a vendor, report the panic value and upgrade to a fixed plugin release"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Go: the adapter already recovers; guard the error path.\nstream, err := adapter.ExecuteStream(ctx, auth, req, opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"stream panic\") {\n        // plugin is now fused; stop retrying and fail over\n        log.Errorf(\"plugin stream panic (fused): %v\", err)\n        return failoverOrError(err)\n    }\n    return err\n}","preventionTips":["Keep plugins pinned to versions tested against the host's plugin API","Stream a canary request after plugin install to catch panics before production traffic","Monitor host logs for fusePlugin events and alert on them"],"tags":["plugin","panic","streaming","circuit-breaker","recover"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}