rathole-org/rathole · error

The token of service

Error message

The token of service {} is not set

What it means

validate_server_config fails when a server-side service has no token and the server has no default_token either. Tokens authenticate clients to services; without one rathole refuses to start rather than run an unauthenticated service. The error names the offending service.

Solutions

  1. Set default_token under [server] to cover all services
  2. Add a per-service token in the service's config block
  3. Ensure the client config uses the identical token for the same service

Example fix

// before
[server]
bind_addr = "0.0.0.0:2333"

[server.services.ssh]
bind_addr = "0.0.0.0:6022"

// after
[server]
bind_addr = "0.0.0.0:2333"
default_token = "secret-token"

[server.services.ssh]
bind_addr = "0.0.0.0:6022"
Defensive patterns

Strategy: validation

Validate before calling

// Python pre-check of server config tokens:
import tomllib
cfg = tomllib.load(open('server.toml','rb'))
srv = cfg['server']
for name, svc in srv.get('services', {}).items():
    if 'token' not in svc and 'default_token' not in srv:
        raise SystemExit(f'Service {name} needs a token or server needs default_token')

Prevention

When it happens

Trigger: Running rathole in server mode with a [server.services.<name>] entry that lacks a token while [server] also lacks default_token; config validation calls bail! at startup.

Common situations: Hand-writing a minimal server.toml and skipping the token; migrating configs and dropping default_token; generating config from templates where the token field was left blank.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of rathole-org/rathole@a292f7ed54 (2026-09-07). Data as JSON: /api/errors/aff8b5204d76945f. Report an issue: GitHub.

Appendix: source

Thrown at src/config.rs:265

        if let Some(client) = config.client.as_mut() {
            Config::validate_client_config(client)?;
        }

        if config.server.is_none() && config.client.is_none() {
            Err(anyhow!("Neither of `[server]` or `[client]` is defined"))
        } else {
            Ok(config)
        }
    }

    fn validate_server_config(server: &mut ServerConfig) -> Result<()> {
        // Validate services
        for (name, s) in &mut server.services {
            s.name = name.clone();
            if s.token.is_none() {
                s.token = server.default_token.clone();
                if s.token.is_none() {
                    bail!("The token of service {} is not set", name);
                }
            }
        }

        Config::validate_transport_config(&server.transport, true)?;

        Ok(())
    }

    fn validate_client_config(client: &mut ClientConfig) -> Result<()> {
        // Validate services
        for (name, s) in &mut client.services {
            s.name = name.clone();
            if s.token.is_none() {
                s.token = client.default_token.clone();
                if s.token.is_none() {
                    bail!("The token of service {} is not set", name);
                }

View on GitHub (pinned to a292f7ed54)