unicity-aos/aos-ce · error

Topic ` ` has empty segments (leading/trailing/consecutive…

Error message

Topic `{key}` has empty segments (leading/trailing/consecutive dots).

What it means

check_topic_shapes validates every [publish] and [subscribe] topic key for well-formedness. A topic key containing empty segments — produced by leading dots, trailing dots, or consecutive dots (`..`) — is malformed because topic matching segments on dots, so the manifest is flagged as an error.

Solutions

  1. Remove leading/trailing dots and collapse consecutive dots in the topic key.
  2. If wildcards are needed, use `*` per segment instead of empty segments.
  3. Re-run validation; also watch for the >8-segment depth warning on the same key.

Example fix

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

Strategy: validation

Validate before calling

// Reject topic keys with empty segments before writing them
fn topic_ok(key: &str) -> bool {
    !key.is_empty() && key.split('.').all(|s| !s.is_empty())
}

Type guard

fn is_well_formed_topic(key: &str) -> bool {
    key.split('.').all(|seg| !seg.is_empty()) && !key.starts_with('.') && !key.ends_with('.')
}

Prevention

When it happens

Trigger: validate_manifest -> check_topic_shapes receives a pub/sub key where `key.split('.')` yields any empty string, e.g. `.tool.v1.request`, `tool..v1`, or `tool.v1.`.

Common situations: String-building topic names with template concatenation leaving double dots, wildcard patterns typed as `tool..v1.*`, or copy-paste artifacts at the edges of the key.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/ddc51902dc9e8de2. Report an issue: GitHub.

Appendix: source

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

                    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(
                format!("Topic `{key}` has more than 8 segments — likely a mistake."),
                "Flatten the topic; deep topic trees usually indicate a naming error.",
            ));
        }
    }
    if out.iter().all(|f| f.level != "error") {
        out.push(Finding::info(
            "No blocking manifest errors found.",
            "Build with `aos capsule build`, then install with `aos capsule install`.",
        ));
    }
}

View on GitHub (pinned to f6f22024fb)