unicity-aos/aos-ce · error

Subscribe `tool.v1.request.describe` is missing — tools…

Error message

Subscribe `tool.v1.request.describe` is missing — tools won't be discoverable.

What it means

A capsule that executes tools must subscribe to `tool.v1.request.describe` so it can answer discovery/describe queries; without it the tool is invisible to discovery tooling. check_tool_bus emits this error when [subscribe] lacks that exact key, alongside guidance to bind it to the `tool_describe` handler.

Solutions

  1. Add `"tool.v1.request.describe" = { wit = ..., handler = "tool_describe" }` to [subscribe].
  2. Ensure the `tool_describe` export exists in the capsule and implements the describe contract.
  3. Re-run validation to confirm both describe publish and subscribe wiring.

Example fix

// before ([subscribe])
"tool.v1.request.execute" = { wit = ..., handler = "tool_execute_fetch" }
// after
"tool.v1.request.execute" = { wit = ..., handler = "tool_execute_fetch" }
"tool.v1.request.describe" = { wit = ..., handler = "tool_describe" }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the describe subscription exists when the capsule executes tools
if executes_tools {
    assert!(subscribe.contains_key("tool.v1.request.describe"), "describe subscription missing");
}

Prevention

When it happens

Trigger: validate_manifest -> check_tool_bus with saw_execute_tool true and no `tool.v1.request.describe` key among sub_keys.

Common situations: Building a new tool capsule from a bare template, removing the describe subscription thinking it is optional, or renaming topics during a protocol migration.

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 unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/a65a658ef0a95dc0. Report an issue: GitHub.

Appendix: source

Thrown at capsules/capsule-forge/src/checks.rs:286

        if !has_handler {
            out.push(Finding::err(
                format!("Subscribe `{key}` has no `handler`."),
                format!("Add `handler = \"tool_execute_{tool}\"` to the `{key}` row."),
            ));
        }
    }

    if saw_execute_tool {
        for required in ["tool.v1.execute.*.result", "tool.v1.response.describe.*"] {
            if !pub_keys.iter().any(|k| k == required) {
                out.push(Finding::err(
                    format!("Mandatory publish key `{required}` is missing."),
                    format!("Add `\"{required}\" = {{ wit = ... }}` to [publish]; tool results/describe break without it."),
                ));
            }
        }
        if !sub_keys.iter().any(|k| k == "tool.v1.request.describe") {
            out.push(Finding::err(
                "Subscribe `tool.v1.request.describe` is missing — tools won't be discoverable.",
                "Add `\"tool.v1.request.describe\" = { wit = ..., handler = \"tool_describe\" }` to [subscribe].",
            ));
        }
    }
}

/// Flag malformed topic keys: empty segments, or absurd depth (>8 segments).
fn check_topic_shapes(pub_keys: &[String], sub_keys: &[String], out: &mut Vec<Finding>) {
    for key in pub_keys.iter().chain(sub_keys) {
        if has_empty_segments(key) {
            out.push(Finding::err(
                format!("Topic `{key}` has empty segments (leading/trailing/consecutive dots)."),
                "Remove the stray dots; every segment between dots must be non-empty.",
            ));
        }
        if key.split('.').count() > 8 {
            out.push(Finding::warn(

View on GitHub (pinned to f6f22024fb)