henrygd/beszel · critical

must set TOKEN or TOKEN_FILE

Error message

must set TOKEN or TOKEN_FILE

What it means

getToken resolves the authentication token in order: already-loaded client.token, then the TOKEN env var, then the file pointed to by TOKEN_FILE. If no token source is configured at all, it throws this error telling the user they must set TOKEN or TOKEN_FILE. The library will not be able to authenticate the WebSocket connection without a token.

Source

Thrown at agent/client.go:84

	client.hubRequest = &common.HubRequest[cbor.RawMessage]{}
	client.fingerprint = agent.getFingerprint()

	return client, nil
}

// getToken returns the token for the WebSocket client.
// It first checks the TOKEN environment variable, then the TOKEN_FILE environment variable.
// If neither is set, it returns an error.
func getToken() (string, error) {
	// get token from env var
	token, _ := utils.GetEnv("TOKEN")
	if token != "" {
		return token, nil
	}
	// get token from file
	tokenFile, _ := utils.GetEnv("TOKEN_FILE")
	if tokenFile == "" {
		return "", errors.New("must set TOKEN or TOKEN_FILE")
	}
	tokenBytes, err := os.ReadFile(tokenFile)
	if err != nil {
		return "", err
	}
	return parseTokenFile(string(tokenBytes), tokenFile)
}

// parseTokenFile reads a single token from TOKEN_FILE.
// Blank lines and comments are ignored. Multiple tokens are rejected because
// the agent supports only one outbound hub connection.
func parseTokenFile(contents, path string) (string, error) {
	var token string
	for line := range strings.Lines(contents) {
		line = strings.TrimSpace(line)
		if len(line) == 0 || strings.HasPrefix(line, "#") {
			continue
		}

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Set TOKEN=<jwt> in the environment before starting the agent.
  2. Or set TOKEN_FILE=/path/to/token and place the token in that file.
  3. Ensure the service definition (systemd unit, Docker env) forwards these variables to the process.
  4. Add a preflight config check that fails with a clear message listing both accepted variables.

Example fix

// before
export HUB_URL="wss://hub"
./agent  # -> must set TOKEN or TOKEN_FILE
// after
export HUB_URL="wss://hub"
export TOKEN_FILE="/etc/agent/token"
./agent
Defensive patterns

Strategy: validation

Validate before calling

token, _ := os.LookupEnv("TOKEN")
tokenFile, hasFile := os.LookupEnv("TOKEN_FILE")
if token == "" && (!hasFile || tokenFile == "") {
	return fmt.Errorf("agent auth not configured: set TOKEN or TOKEN_FILE")
}
if hasFile && tokenFile != "" {
	if _, err := os.Stat(tokenFile); err != nil {
		return fmt.Errorf("TOKEN_FILE %s not readable: %w", tokenFile, err)
	}
}

Try / catch

client, err := newWebSocketClient(agent)
if err != nil {
	if strings.Contains(err.Error(), "TOKEN or TOKEN_FILE") {
		log.Fatal("auth config missing: provide TOKEN env var or a readable TOKEN_FILE")
	}
	log.Fatal(err)
}

Prevention

When it happens

Trigger: newWebSocketClient calls getToken with no client.token set, TOKEN unset, and TOKEN_FILE unset or empty.

Common situations: Fresh install where only HUB_URL was configured; token provisioning step skipped in CI; running the agent under a service manager that sanitizes env vars; TOKEN_FILE set but pointing to an empty path due to another config bug (note: a set-but-wrong TOKEN_FILE produces a different read error).

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/ba8907e961d140a0. Report an issue: GitHub.