{"id":"402193dc2f63340b","repo":"docker/cli","slug":"plugin-server-is-closed","errorCode":null,"errorMessage":"plugin server is closed","messagePattern":"plugin server is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"cli-plugins/socket/socket.go","lineNumber":79,"sourceCode":"\tconns  []net.Conn\n\tl      *net.UnixListener\n\th      func(net.Conn)\n\tclosed bool\n}\n\nfunc (pl *PluginServer) accept() error {\n\tconn, err := pl.l.Accept()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpl.mu.Lock()\n\tdefer pl.mu.Unlock()\n\n\tif pl.closed {\n\t\t// Handle potential race between Close and accept.\n\t\tconn.Close()\n\t\treturn errors.New(\"plugin server is closed\")\n\t}\n\n\tpl.conns = append(pl.conns, conn)\n\n\tgo pl.h(conn)\n\treturn nil\n}\n\n// Addr returns the [net.Addr] of the underlying [net.Listener].\nfunc (pl *PluginServer) Addr() net.Addr {\n\treturn pl.l.Addr()\n}\n\n// Close ensures that the server is no longer accepting new connections and\n// closes all existing connections. Existing connections will receive [io.EOF].\n//\n// The error value is that of the underlying [net.Listener.Close] call.\nfunc (pl *PluginServer) Close() error {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/docker/cli/blob/e9452d6e785f6e365712b9d71bd7517591773c86/cli-plugins/socket/socket.go#L61-L97","documentation":"PluginServer is the unix-socket server the docker CLI uses to communicate with a running CLI plugin (e.g. for signal/teardown coordination). accept() returns this error when a new connection is accepted concurrently with Close(): after taking the mutex it finds pl.closed set, closes the just-accepted connection, and reports that the server is shut down. It exists to resolve the inherent race between Listener.Accept and Close cleanly.","triggerScenarios":"PluginServer.Close() is called (typically when the plugin command finishes or the CLI is tearing down) while the accept loop has just returned a new net.Conn from the UnixListener — the connection is dropped and accept returns this error, terminating the accept loop.","commonSituations":"Normal shutdown ordering — this is largely an expected, benign race-resolution path; it can surface in logs when a plugin (buildx/compose) exits while another process attempts to connect to its socket, or in tests that Close the server while dialing concurrently.","solutions":["Treat the error as a normal shutdown signal: exit the accept loop without logging it as a failure","Ensure clients stop dialing the plugin socket once the plugin command completes","If seen unexpectedly mid-operation, check for premature Close() calls in teardown ordering (Close before in-flight connections were expected)"],"exampleFix":"// before\nfor {\n    if err := pl.accept(); err != nil {\n        log.Errorf(\"accept failed: %v\", err)\n    }\n}\n// after\nfor {\n    if err := pl.accept(); err != nil {\n        return // server closed or listener error; stop accepting\n    }\n}","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":["docker-cli","cli-plugins","unix-socket","concurrency","shutdown"],"analyzedSha":"e9452d6e785f6e365712b9d71bd7517591773c86","analyzedAt":"2026-08-01T07:09:22.474Z","schemaVersion":2}