fatedier/frp · error

exec command cannot be empty

Error message

exec command cannot be empty

What it means

Thrown by ExecSource.Validate() when a ValueSource of type "exec" has an empty Command. ExecSource.Resolve launches the command via exec.CommandContext and captures stdout, so a command is mandatory; this check fires before validation of args or env entries.

Source

Thrown at pkg/config/v1/value_source.go:123

	}

	content, err := os.ReadFile(f.Path)
	if err != nil {
		return "", fmt.Errorf("failed to read file %s: %v", f.Path, err)
	}

	// Trim whitespace, which is important for file-based tokens
	return strings.TrimSpace(string(content)), nil
}

// Validate validates the ExecSource configuration.
func (e *ExecSource) Validate() error {
	if e == nil {
		return errors.New("execSource cannot be nil")
	}

	if e.Command == "" {
		return errors.New("exec command cannot be empty")
	}

	for _, env := range e.Env {
		if env.Name == "" {
			return errors.New("exec env name cannot be empty")
		}
		if strings.Contains(env.Name, "=") {
			return errors.New("exec env name cannot contain '='")
		}
	}
	return nil
}

// Resolve reads and returns the content captured from stdout of launched subprocess.
func (e *ExecSource) Resolve(ctx context.Context) (string, error) {
	if err := e.Validate(); err != nil {
		return "", err
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set tokenSource.exec.command to an executable that prints the token to stdout
  2. Check TOML/YAML indentation: command must be a key inside [auth.tokenSource.exec]
  3. Confirm the command is on PATH or use an absolute path
  4. Validate with frpc verify -c ./frpc.toml before restart

Example fix

# before
[auth.tokenSource.exec]
args = ["--json"]

# after
[auth.tokenSource.exec]
command = "cloudctl"
args = ["--json", "token", "print"]
Defensive patterns

Strategy: validation

Validate before calling

if vs := cfg.Auth.TokenSource; vs != nil && vs.Type == "exec" && vs.Exec != nil {
    if vs.Exec.Command == "" {
        return fmt.Errorf("tokenSource.exec.command is empty")
    }
}

Type guard

func hasCommand(e *v1.ExecSource) bool {
    return e != nil && strings.TrimSpace(e.Command) != ""
}

Prevention

When it happens

Trigger: tokenSource.exec table present but command key missing or empty; Go literal v1.ExecSource{Args: []string{"--flag"}} with Command unset; YAML indentation mistake putting command under the wrong table so it is not decoded.

Common situations: Commenting out the command while testing; config templates that inject the command from an environment variable that is unset; switching from file to exec token source and only copying args/env.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/f817eb2a63c08734. Report an issue: GitHub.