hashicorp/nomad · error
%q has already been closed
Error message
%q has already been closed
What it means
errorIfClosed guards every operation on a netNS handle; this error is returned when Set(), Do(), or any use after Close() is attempted on an already-closed NetNS. Once closed, the underlying file is closed and the handle cannot be reused.
Source
Thrown at client/lib/nsutil/ns_linux.go:169
fd, err := os.Open(nspath)
if err != nil {
return nil, err
}
return &netNS{file: fd}, nil
}
func (ns *netNS) Path() string {
return ns.file.Name()
}
func (ns *netNS) Fd() uintptr {
return ns.file.Fd()
}
func (ns *netNS) errorIfClosed() error {
if ns.closed {
return fmt.Errorf("%q has already been closed", ns.file.Name())
}
return nil
}
func (ns *netNS) Do(toRun func(NetNS) error) error {
if err := ns.errorIfClosed(); err != nil {
return err
}
containedCall := func(hostNS NetNS) error {
threadNS, err := GetCurrentNS()
if err != nil {
return fmt.Errorf("failed to open current netns: %v", err)
}
defer threadNS.Close()
// switch to target namespace
if err = ns.Set(); err != nil {View on GitHub (pinned to 482b49bf1a)
Solutions
- Do not call Close() until all Set()/Do() usage of the handle is finished
- Re-open the namespace with GetNS(nspath) or GetCurrentNS() instead of reusing the closed handle
- Audit defer ordering so Close() is the last action referencing the handle
- Guard concurrent use with ownership/lifetime management rather than sharing the handle across goroutines after close
Example fix
// before hostNS, _ := GetCurrentNS() defer hostNS.Close() // ... later in another goroutine hostNS.Set() // "has already been closed" // after hostNS, _ := GetCurrentNS() err := doWork(hostNS) // all use happens here defer hostNS.Close() // close only after last use
Defensive patterns
Strategy: validation
Validate before calling
func ensureOpen(ns *netNS) error {
if ns == nil || ns.closed {
return errors.New("netns closed: reopen with GetNS/GetCurrentNS before use")
}
return nil
} Type guard
func isOpen(ns NetNS) bool {
n, ok := ns.(*netNS)
return ok && !n.closed
} Try / catch
if err := ns.Set(); err != nil {
if strings.Contains(err.Error(), "has already been closed") {
ns, err = GetNS(nsPath) // reopen instead of failing
if err != nil { return err }
return ns.Set()
}
return err
} Prevention
- Close NetNS handles only after all Set()/Do() usage completes
- Order defers so Close() runs last
- Do not share a NetNS across goroutines after initiating teardown
- Reopen via GetNS instead of caching handles long-term
When it happens
Trigger: Calling Set() or Do() on a NetNS after calling Close() on it; calling Set() on the saved hostNS/threadNS handle after a deferred Close() already ran; reusing a NetNS obtained from GetNS/GetCurrentNS after the defer cleanup fired.
Common situations: Deferred Close() firing before a later goroutine/closure uses the handle; calling Do() twice where the first defer closed the handle; storing a NetNS long-term and using it after a shutdown path closed it.
Related errors
- mount --make-rshared %s failed: %q
- mount --rbind %s %s failed: %q
- failed to get the current netns: %v
- error from unshare: %v
- failed to bind mount ns at %s: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/ec0ffd5a729dc7a2.
Report an issue: GitHub.