astrid-runtime/astrid · error

a topic is required (positional ` ` or `--topic `)

Error message

a topic is required (positional `<topic>` or `--topic <topic>`)

What it means

Resolution guard in Args::resolve_topic: neither the positional `<topic>` argument nor the `--topic` flag was supplied. clap's conflicts_with already rejects supplying both, so this error exclusively covers the neither-supplied case. Emitting an event requires exactly one topic to build the `{host}.v1.hook.{session}.{event}` topic name.

Solutions

  1. Pass the topic positionally, e.g. astrid-emit sage.v1.hook.before_tool_call
  2. Or pass it as a flag: astrid-emit --topic sage.v1.hook.before_tool_call
  3. Check the wrapper script or host hook config that dropped the topic argument
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/astrid-emit/src/lib.rs:141 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/248f05a958a1174e. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-emit/src/lib.rs:141

    Ok(format!("{host}.v1.hook.{session}.{event}"))
}

impl Args {
    /// Resolve the single topic from whichever form was supplied.
    ///
    /// `clap`'s `conflicts_with` already rejects supplying both, so the
    /// only failure this surfaces is supplying neither.
    ///
    /// # Errors
    ///
    /// Returns an error if no topic was supplied (neither positional
    /// nor `--topic`).
    pub fn resolve_topic(self) -> Result<String> {
        match (self.topic_positional, self.topic_flag) {
            (Some(t), None) | (None, Some(t)) => Ok(t),
            (None, None) => {
                anyhow::bail!("a topic is required (positional `<topic>` or `--topic <topic>`)")
            },
            // Unreachable in practice: clap's `conflicts_with` rejects
            // both-supplied before we get here. Kept exhaustive so the
            // match doesn't rely on that invariant silently.
            (Some(_), Some(_)) => {
                anyhow::bail!("specify the topic exactly once (positional OR --topic, not both)")
            },
        }
    }
}

/// Outcome of an [`emit`] call: what to print to stderr (if anything)
/// and which process exit code to return. The fixed `{"continue":true}`
/// stdout line is the caller's responsibility and is emitted
/// unconditionally regardless of this outcome.
#[derive(Debug)]
#[must_use]
pub struct Outcome {

View on GitHub (pinned to affd8760f4)