hashicorp/nomad · error

failed to load CNI config file %s: %v

Error message

failed to load CNI config file %s: %v

What it means

Same CNI fingerprinter, but for single-network config files (e.g. .conf or .json without the .conflist suffix). libcni.ConfFromFile parses the file into a single CNI config; a read error or invalid single-config JSON aborts the fingerprint with this wrapped error.

Source

Thrown at client/fingerprint/cni.go:55

	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{}{}
		}
	}

	var nodeNetworks structs.Networks
	var newNodeNetworks []*structs.NodeNetworkResource

	for name := range networks {
		mode := fmt.Sprintf("cni/%s", name)
		nodeNetworks = append(nodeNetworks, &structs.NetworkResource{
			Mode: mode,
		})
		newNodeNetworks = append(newNodeNetworks, &structs.NodeNetworkResource{

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Validate the file with jq and ensure it has name, cniVersion, and type fields
  2. Remove non-CNI files (backups, docs, .tmp) from the CNI conf directory
  3. Fix file permissions so Nomad can read it
  4. Reinstall/regenerate the config from your CNI plugin's docs

Example fix

// before (invalid single conf)
{"name":"bridge"}
// after
{"name":"bridge","cniVersion":"0.4.0","type":"bridge"}
Defensive patterns

Strategy: validation

Validate before calling

func validateCNIConf(path string) error { b, err := os.ReadFile(path); if err != nil { return err }; var c struct{ Name string `json:"name"`; CNIVersion string `json:"cniVersion"`; Type string `json:"type"` }; if err := json.Unmarshal(b, &c); err != nil { return err }; if c.Name == "" || c.CNIVersion == "" || c.Type == "" { return fmt.Errorf("missing required fields in %s", path) }; return nil }

Type guard

func isValidCNIConf(path string) bool { b, err := os.ReadFile(path); if err != nil { return false }; var c struct{ Name, CNIVersion, Type string }; return json.Unmarshal(b, &c) == nil && c.Name != "" && c.CNIVersion != "" && c.Type != "" }

Prevention

When it happens

Trigger: A non-.conflist file in the CNI conf dir is unreadable, malformed JSON, lacks required fields (name, cniVersion, type), or contains something unexpected like a README.txt or backup copy.

Common situations: Editors leaving .conf.bak or .json.orig files in /opt/cni/net.d; templating tools writing partial configs; missing "type" field in a bridge plugin config; permission changes from config management.

Related errors


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