{"record":{"id":"dac482ea90d9b2df","repo":"hashicorp/nomad","slug":"failed-to-close-q-v","errorCode":null,"errorMessage":"Failed to close %q: %v","messagePattern":"Failed to close %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"client/lib/nsutil/ns_linux.go","lineNumber":54,"sourceCode":"\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\treturn GetNS(getCurrentThreadNetNSPath())\n}\n\nfunc getCurrentThreadNetNSPath() string {\n\t// /proc/self/ns/net returns the namespace of the main thread, not\n\t// of whatever thread this goroutine is running on.  Make sure we\n\t// use the thread's net namespace since the thread is switching around\n\treturn fmt.Sprintf(\"/proc/%d/task/%d/ns/net\", os.Getpid(), unix.Gettid())\n}\n\nfunc (ns *netNS) Close() error {\n\tif err := ns.errorIfClosed(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := ns.file.Close(); err != nil {\n\t\treturn fmt.Errorf(\"Failed to close %q: %v\", ns.file.Name(), err)\n\t}\n\tns.closed = true\n\n\treturn nil\n}\n\nfunc (ns *netNS) Set() error {\n\tif err := ns.errorIfClosed(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil {\n\t\treturn fmt.Errorf(\"Error switching to ns %v: %v\", ns.file.Name(), err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/lib/nsutil/ns_linux.go#L36-L72","documentation":"netNS wraps an *os.File pointing at the namespace; Close closes that file handle. If the underlying file close fails, the library reports 'Failed to close <path>'. This is uncommon on Linux but indicates the descriptor could not be released cleanly.","triggerScenarios":"Calling ns.Close() (also invoked via deferred cleanup in NewNS) when ns.file.Close() returns an error — e.g. the file was already closed at the fd level, I/O error, or fd closed elsewhere causing EBADF. Note Close returns errorIfClosed's error first if called twice.","commonSituations":"Calling Close twice on the same netns handle (second call returns 'netns already closed', not this error); external code closing the same fd; resource-limit related edge cases during heavy churn of namespaces.","solutions":["Call Close exactly once per netns handle and rely on the closed flag for idempotency","Do not close the underlying fd from elsewhere (avoid sharing the *os.File outside the library)","Check for fd leaks/tampering if EBADF appears; audit any code that duplicates or reaps fds","If this surfaces in tests, ensure teardown order does not close the ns before the library does"],"exampleFix":"// before: double close\nns.Close()\nerr := ns.Close() // already-closed error path\n// after: close once, defer handles it\nfunc work(ns nsutil.NetNS) error {\n    defer ns.Close()\n    return nil\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isNetnsOpen(ns nsutil.NetNS) bool { return ns != nil && !ns.IsClosed() }","tryCatchPattern":"if err := ns.Close(); err != nil {\n    log.Printf(\"warn: netns close failed for handle: %v (possible double-close or external fd close)\", err)\n}","preventionTips":["Close each netns handle exactly once; prefer defer ns.Close() at the point of creation","Never share or close the underlying *os.File outside the library","Check errorIfClosed before reusing a handle after Close","Audit fd handling code that might close foreign descriptors (EBADF source)"],"tags":["linux","network-namespace","file-descriptor","resource-cleanup"],"backgroundTag":"fd-close-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}