hashicorp/nomad · error

intro token file is a directory

Error message

intro token file is a directory

What it means

After stat succeeds, readIntroTokenFile checks fileStat.IsDir(); if the intro token path is a directory, it returns this plain error because a directory cannot hold token content. The intro token must be a regular file whose contents are the token.

Source

Thrown at command/agent/agent.go:879

	rootFile, err := os.OpenInRoot(cfg.StateDir, "intro_token.jwt")
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}

	fileStat, err := rootFile.Stat()
	if err != nil {
		return fmt.Errorf("failed to stat intro token file: %w", err)
	}

	// If the file exists and is a file, attempt to read the contents and set
	// the intro token. Any error is logged for the operator to investigate but
	// does not block the agent from starting.
	if fileStat.IsDir() {
		return fmt.Errorf("intro token file is a directory")
	}

	content, err := helper.ReadFileContent(rootFile)
	if err != nil {
		return fmt.Errorf("failed to read intro token file: %w", err)
	}

	cfg.IntroToken = strings.TrimSpace(string(content))
	return nil
}

// convertClientConfig takes an agent config and log output and returns a client
// Config. There may be missing fields that must be set by the agent. To do this
// call finalizeServerConfig
func convertClientConfig(agentConfig *Config) (*clientconfig.Config, error) {
	// Set up the configuration
	conf := agentConfig.ClientConfig
	if conf == nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Point intro_token_file at the actual regular file, not its parent directory.
  2. For volume-mounted secrets, reference the key file inside the mount (e.g. /secrets/intro-token, not /secrets).
  3. Pre-provision a file at the path if the mount point is a directory by design.

Example fix

// before
client { intro_token_file = "/var/run/secrets/nomad" } // directory
// after
client { intro_token_file = "/var/run/secrets/nomad/intro-token" }
Defensive patterns

Strategy: validation

Validate before calling

fi, err := os.Stat(path)
if err != nil {
  return err
}
if fi.IsDir() {
  return fmt.Errorf("intro_token_file %q is a directory; point to the token file itself", path)
}

Prevention

When it happens

Trigger: client.intro_token_file pointing at a directory (e.g. a secrets mount point like /var/run/secrets/ or the token's parent directory instead of the file), invoked from finalizeClientConfig.

Common situations: Kubernetes secret volume mounts where the user gives the volume dir rather than the key file inside it; path expansion mistakes in config tooling; mounted paths that are always directories.

Related errors


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