hashicorp/nomad · error

unable to read usable cores: %w

Error message

unable to read usable cores: %w

What it means

cpuSet reads the effective CPU set of the task from cpuset cgroup file (cpuset.cpus.effective or cpuset.cpus) under the task's CpusetCgroupPath. If os.ReadFile of that file fails, Nomad cannot determine which cores the container may use and returns this wrapped error. It surfaces during createContainerConfig, i.e. at task start.

Source

Thrown at drivers/docker/driver.go:1015

		return minCPUShares
	}
	return result
}

// cpuSet reads the available cores from the nomad client in order to assign
// them to the new container, ensuring all tasks run in nomad assigned cpus.
func (d *Driver) cpuSet(taskResources *drivers.Resources) (string, error) {

	if taskResources.LinuxResources != nil &&
		taskResources.LinuxResources.CpusetCgroupPath == "" {
		return "", nil
	}

	// read the current value of usable cores
	source := filepath.Join(taskResources.LinuxResources.CpusetCgroupPath, effectiveCpusetFile())
	b, err := os.ReadFile(source)
	if err != nil {
		return "", fmt.Errorf("unable to read usable cores: %w", err)
	}

	return idset.Parse[hw.CoreID](string(b)).String(), nil
}

func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *TaskConfig,
	imageID string) (createContainerOptions, error) {

	logger := d.logger.With("task_name", task.Name)
	c := createContainerOptions{}

	// ensure that PortMap variables are populated early on
	task.Env = taskenv.SetPortMapEnvs(task.Env, driverConfig.PortMap)

	if task.Resources == nil {
		// Guard against missing resources. We should never have been able to
		// schedule a job without specifying this.
		logger.Error("task.Resources is empty")

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Verify /sys/fs/cgroup is mounted and contains cpuset.cpus(.effective) under the task's cgroup path; fix mounts (e.g. bind-mount cgroups into the Nomad container).
  2. Check the cgroup v1 cpuset controller is enabled and attached to the client's cgroup hierarchy (cat /proc/cgroups, check cpuset subsystem).
  3. Confirm file permissions — the nomad user must be able to read the cgroup files; check AppArmor/SELinux denials.
  4. Upgrade Nomad / check effectiveCpusetFile behavior matches your cgroup version (cgroup.controllers present means v2).

Example fix

// docker run for nomad agent
// before
docker run nomad
// after
docker run -v /sys/fs/cgroup:/sys/fs/cgroup:ro nomad
Defensive patterns

Strategy: validation

Validate before calling

function validateCgroupReadability() {
  const base = '/sys/fs/cgroup';
  const files = require('fs').existsSync(base + '/cgroup.controllers')
    ? ['cgroup.cpuset.cpus.effective']
    : ['cpuset/cpuset.cpus'];
  for (const f of files) {
    require('fs').accessSync(base + '/' + f);
  }
  return true;
}

Try / catch

try {
  await driver.StartTask(taskCfg);
} catch (err) {
  if (/unable to read usable cores/.test(err.message)) {
    // cgroup path missing/unreadable — check mounts and controller availability
    log.error('cpuset cgroup unreadable; verify /sys/fs/cgroup mount and cpuset controller', { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: The cpuset cgroup path for the task does not exist or is unreadable: cgroup v1/v2 mismatch (effectiveCpusetFile picks the wrong file), task cgroup already destroyed, cgroup filesystem not mounted, or permission denied reading /sys/fs/cgroup.

Common situations: Running Nomad in containers without /sys/fs/cgroup mounted; cgroup v1 systems where cpuset controller is not attached to the Nomad client's cgroup; hardened hosts restricting /sys reads; race where the task's cgroup was cleaned up before docker start.

Related errors


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