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
- Remove leading/trailing dots and collapse consecutive dots in the topic key.
- If wildcards are needed, use `*` per segment instead of empty segments.
- 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
- Build topics with a helper that joins non-empty segments with '.'.
- Add a unit test per topic constant asserting no empty segments.
- Keep depth at or under 8 segments to also avoid the depth warning.
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
- Subscribe ` ` has priority outside the u32 range.
- Subscribe ` ` priority must be an integer.
- Subscribe ` ` has no `handler`.
- Mandatory publish key
- Subscribe `tool.v1.request.describe` is missing — tools…
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)