hashicorp/nomad · error

failed to detect CNI conf files: %v

Error message

failed to detect CNI conf files: %v

What it means

The CNI fingerprinter calls libcni.ConfFiles on the configured CNI config directory (client config cni_config_dir) to enumerate .conf/.conflist/.json network definitions. This error means libcni.ConfFiles itself failed - distinct from the directory not existing (which is handled and just skips detection). ConfFiles fails when the path exists but is not a directory, or when reading it errors (permissions, I/O).

Source

Thrown at client/fingerprint/cni.go:38

	logger hclog.Logger
}

func NewCNIFingerprint(logger hclog.Logger) Fingerprint {
	return &CNIFingerprint{logger: logger}
}

func (f *CNIFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error {
	confDir := req.Config.CNIConfigDir
	networks := map[string]struct{}{}
	if _, err := os.Stat(confDir); os.IsNotExist(err) {
		f.logger.Debug("CNI config dir is not set or does not exist, skipping", "cni_config_dir", confDir)
		resp.Detected = false
		return nil
	}

	files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"})
	if err != nil {
		return fmt.Errorf("failed to detect CNI conf files: %v", err)
	}

	for _, confFile := range files {
		if strings.HasSuffix(confFile, ".conflist") {
			confList, err := libcni.ConfListFromFile(confFile)
			if err != nil {
				return fmt.Errorf("failed to load CNI config list file %s: %v", confFile, err)
			}
			if _, ok := networks[confList.Name]; ok {
				f.logger.Warn("duplicate CNI config names found, ignoring file", "name", confList.Name, "file", confFile)
				continue
			}
			networks[confList.Name] = struct{}{}
		} else {
			conf, err := libcni.ConfFromFile(confFile)
			if err != nil {
				return fmt.Errorf("failed to load CNI config file %s: %v", confFile, err)
			}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Verify cni_config_dir points to a real directory: 'ls -ld /opt/cni/config' (or your configured path).
  2. Fix ownership/permissions so the Nomad agent user can read it (e.g. chmod/chown the directory).
  3. Correct the client config if it points at a file; restart the Nomad client after fixing.
  4. Ensure the path is not a broken or wrong symlink target.

Example fix

HCL
# before
client {
  cni_config_dir = "/opt/cni/bin/myconf.conflist"  # a file, not a dir
}
# after
client {
  cni_config_dir = "/opt/cni/net.d"  # existing, readable directory
}
# then: sudo chown nomad:nomad /opt/cni/net.d && sudo systemctl restart nomad
Defensive patterns

Strategy: validation

Validate before calling

// caller-side preflight before relying on CNI fingerprint
confDir := cfg.Client.CNIConfigDir
if fi, err := os.Stat(confDir); err != nil {
    return fmt.Errorf("CNI config dir %q unreadable: %w", confDir, err)
} else if !fi.IsDir() {
    return fmt.Errorf("CNI config dir %q is not a directory", confDir)
}
// also ensure readability: os.ReadDir(confDir) as a dry run

Try / catch

if err := fingerprintErr; err != nil {
    if strings.Contains(err.Error(), "failed to detect CNI conf files") {
        return fmt.Errorf("check cni_config_dir points to an existing, readable DIRECTORY owned by the nomad user: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: client config cni_config_dir points to a path that exists (os.Stat succeeded) but is a regular file, a symlink to a non-directory, or cannot be read by the Nomad agent user (permission denied); libcni.ConfFiles returns the underlying os error which is wrapped here.

Common situations: cni_config_dir misconfigured to point at a file (e.g. the binary or a conf file itself) instead of a directory; directory owned by root with restrictive permissions while Nomad runs as an unprivileged user; directory removed/replaced with a file after client start.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/775fd3c527c4e649. Report an issue: GitHub.