cloudflare/cloudflared · error

could not find program data directory, %s env var must be se

Error message

could not find program data directory, %s env var must be set

What it means

cloudflared stores its Windows service configuration (including the tunnel token file) under %PROGRAMDATA%\<configDirName>. getConfigDir reads the PROGRAMDATA environment variable and throws this error when it is unset, because it cannot locate the program-data directory. It is called by installWindowsService and uninstallWindowsService during service installation/removal.

Source

Thrown at cmd/cloudflared/windows_service.go:295

			}
		case err := <-errC:
			if err != nil {
				elog.Error(1, fmt.Sprintf("cloudflared terminated with error %v", err))
				ssec = true
				errno = 1
			} else {
				elog.Info(1, "cloudflared terminated without error")
				errno = 0
			}
			return
		}
	}
}

func getConfigDir() (string, error) {
	progDat, progDatSet := os.LookupEnv(programDataEnvVar)
	if !progDatSet {
		return "", fmt.Errorf("could not find program data directory, %s env var must be set", programDataEnvVar)
	}

	return filepath.Join(progDat, configDirName), nil
}

func installWindowsService(c *cli.Context) error {
	zeroLogger := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)

	zeroLogger.Info().Msg("Installing cloudflared Windows service")
	exepath, err := os.Executable()
	if err != nil {
		return errors.Wrap(err, "Cannot find path name that start the process")
	}
	m, err := mgr.Connect()
	if err != nil {
		return errors.Wrap(err, "Cannot establish a connection to the service control manager")
	}
	defer m.Disconnect()

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Ensure PROGRAMDATA is set system-wide (it is by default: C:\ProgramData) — check `echo %PROGRAMDATA%`
  2. Run the install/uninstall from a normal elevated cmd/PowerShell session, not a stripped environment
  3. Set it explicitly for the invocation: set PROGRAMDATA=C:\ProgramData && cloudflared service install <args>
  4. If launched by another service/scheduler, add PROGRAMDATA to that task's environment

Example fix

# before (stripped shell)
cloudflared service install <token>
// after (cmd)
set PROGRAMDATA=C:\ProgramData
cloudflared service install <token>
Defensive patterns

Strategy: validation

Validate before calling

# cmd pre-check
if "%PROGRAMDATA%"=="" (echo PROGRAMDATA is not set & exit /b 1)
# PowerShell pre-check
if (-Not $env:PROGRAMDATA) { throw 'PROGRAMDATA env var must be set before service install' }

Try / catch

if err := installWindowsService(ctx); err != nil {
	if strings.Contains(err.Error(), "program data directory") {
		return fmt.Errorf("PROGRAMDATA env var missing; run from a normal elevated shell: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: os.LookupEnv("PROGRAMDATA") returns ok=false — the variable is not defined in the process environment when service install/uninstall runs.

Common situations: Launching cloudflared from a stripped environment (custom service runner, scheduled task, or SSH session where the user profile/PROGRAMDATA is not populated); running inside a container or under a tool that clears the environment; a corrupted system environment variable block.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/e0689d02844dda59. Report an issue: GitHub.