zeroclaw-labs/zeroclaw · error · anyhow::Error

matrix: `homeserver` is required

Error message

matrix: `homeserver` is required

What it means

The Matrix channel constructor validates config up front: channels.matrix.homeserver must be a non-empty, non-whitespace string, because every SDK call (login, sync, send) needs the homeserver base URL. An empty homeserver aborts construction immediately, before any login attempt - even when an access token is configured, since a token does not imply a server.

Source

Thrown at crates/zeroclaw-channels/src/matrix.rs:3987

    undecryptable_seen: Arc<TokioMutex<HashSet<OwnedEventId>>>,
    /// Resolved `ack_reactions` for this Matrix instance — the
    /// per-channel `MatrixConfig.ack_reactions` override falls back to
    /// `[channels].ack_reactions` here at construction time, so the
    /// read site doesn't need to re-resolve on every reaction.
    ack_reactions: bool,
}

impl MatrixChannel {
    /// Validate config and prepare the channel. The SDK Client is built lazily
    /// on first `listen()` or `send()` call.
    pub fn new(
        config: MatrixConfig,
        alias: impl Into<String>,
        peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync>,
        state_dir: PathBuf,
    ) -> Result<Self> {
        if config.homeserver.trim().is_empty() {
            bail!("matrix: `homeserver` is required");
        }
        let has_token = config
            .access_token
            .as_deref()
            .is_some_and(|t| !t.trim().is_empty());
        let has_password = config
            .password
            .as_deref()
            .is_some_and(|p| !p.trim().is_empty());
        if !has_token && !has_password {
            bail!("matrix: configure either `access_token` or `password`");
        }
        let ack_reactions = config.ack_reactions.unwrap_or(true);
        let streaming_state = streaming::State::for_stream_mode(config.stream_mode);
        Ok(Self {
            config: Arc::new(config),
            alias: alias.into(),
            peer_resolver,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set channels.matrix.homeserver to the homeserver's client API base, e.g. https://matrix.example.org.
  2. Check the rendered config for empty values from unset environment variables - the trim check makes whitespace count as missing.
  3. Homeserver is mandatory in every auth mode (token or password); it cannot be omitted in favor of access_token.
  4. Add a config lint that fails builds when required Matrix keys are blank.

Example fix

# before
[channels.matrix]
access-token = "syt_..."

# after
[channels.matrix]
homeserver = "https://matrix.example.org"
access-token = "syt_..."
Defensive patterns

Strategy: validation

Validate before calling

fn homeserver_configured(cfg: &MatrixConfig) -> bool {
    !cfg.homeserver.trim().is_empty()
}

assert!(homeserver_configured(&config), "channels.matrix.homeserver is required");

Type guard

fn required_homeserver(cfg: &MatrixConfig) -> Option<&str> {
    let h = cfg.homeserver.trim();
    (!h.is_empty()).then_some(h)
}

Prevention

When it happens

Trigger: Constructing the Matrix channel with channels.matrix.homeserver absent, empty, or whitespace-only - typically an env-var interpolation (e.g. ${MATRIX_HOMESERVER}) that produced an empty string, or a misspelled/removed config key.

Common situations: New deployment with homeserver forgotten; templating emitting "" for unset variables; config key renamed across a ZeroClaw upgrade; local dev config trimmed down too far.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/ad25599601d1a7b8. Report an issue: GitHub.