hashicorp/nomad · error

failed to load CNI config list file %s: %v

Error message

failed to load CNI config list file %s: %v

What it means

Nomad's CNI fingerprinter scans the CNI config directory (default /opt/cni/net.d) for network configurations. When it encounters a *.conflist file it calls libcni.ConfListFromFile to parse it; if the file cannot be read or parsed as a valid CNI conflist JSON, the fingerprint aborts with this wrapped error.

Source

Thrown at client/fingerprint/cni.go:45

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)
			}
			if _, ok := networks[conf.Network.Name]; ok {
				f.logger.Warn("duplicate CNI config names found, ignoring file", "name", conf.Network.Name, "file", confFile)
				continue
			}
			networks[conf.Network.Name] = struct{}{}
		}
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Validate the JSON of the failing .conflist file (jq . <file>) and fix syntax/missing fields
  2. Check file permissions so the Nomad agent user can read the file
  3. Remove or rename the broken .conflist if the plugin is no longer used
  4. Confirm the cni_config_dir client config points at the directory actually populated by your CNI plugins
  5. Reinstall the CNI plugin package to regenerate correct conflist files

Example fix

// before (broken conflist)
{"name":"mynet","cniVersion":"0.4.0"} // missing "plugins"
// after
{"name":"mynet","cniVersion":"0.4.0","plugins":[{"type":"bridge","bridge":"cni0"}]}
Defensive patterns

Strategy: validation

Validate before calling

for _, f := range cniFiles { if strings.HasSuffix(f, ".conflist") { if _, err := os.Stat(f); err != nil { return err }; b, err := os.ReadFile(f); if err != nil { return err }; var cl struct{ Name string `json:"name"`; Plugins []json.RawMessage `json:"plugins"` }; if err := json.Unmarshal(b, &cl); err != nil || cl.Name == "" || len(cl.Plugins) == 0 { return fmt.Errorf("invalid conflist %s", f) } } }

Type guard

func isValidConflist(path string) bool { b, err := os.ReadFile(path); if err != nil { return false }; var cl struct{ Name string `json:"name"`; Plugins []json.RawMessage `json:"plugins"` }; return json.Unmarshal(b, &cl) == nil && cl.Name != "" && len(cl.Plugins) > 0 }

Prevention

When it happens

Trigger: A .conflist file exists in the CNI conf dir but is malformed JSON, missing required fields (name/plugins), unreadable due to permissions, or is a dangling symlink/broken file.

Common situations: Partially written or truncated conflist after a failed CNI plugin install; hand-edited conflist with JSON syntax errors; wrong file ownership after Ansible/automation deployment; node upgraded and old plugin config incompatible.

Related errors


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