swc-project/swc · warning · Error
Start tag "{tag_name}" seen without a "ruby" element being o
Error message
Start tag "{tag_name}" seen without a "ruby" element being open What it means
A start tag `rb` or `rtc` was seen while no `ruby` element is in scope (parser/mod.rs:4066-4081). Ruby annotation tags are only meaningful inside `<ruby>`, so the spec makes this a parse error (`StartTagSeenWithoutRuby`); the parser still inserts the element, now as ordinary body content.
Source
Thrown at crates/swc_html_parser/src/parser/mod.rs:4072
// implied end tags. If the current node is not now a ruby element, this is a
// parse error.
//
// Insert an HTML element for the token.
Token::StartTag {
tag_name,
is_self_closing,
..
} if matches!(&**tag_name, "rb" | "rtc") => {
let is_scope = self.open_elements_stack.has_in_scope("ruby");
if is_scope {
self.open_elements_stack.generate_implied_end_tags();
}
match self.open_elements_stack.items.last() {
Some(node) if !is_html_element!(node, "ruby") => {
if !is_scope {
self.errors.push(Error::new(
token_and_info.span,
ErrorKind::StartTagSeenWithoutRuby(tag_name.clone()),
));
} else {
self.errors.push(Error::new(
token_and_info.span,
ErrorKind::UnclosedChildrenInRuby,
));
}
}
_ => {}
}
self.insert_html_element(token_and_info)?;
maybe_allow_self_closing!(is_self_closing, tag_name);
}
// A start tag whose tag name is one of: "rp", "rt"
//View on GitHub (pinned to 5176682b65)
Solutions
- Wrap the annotation markup in `<ruby>...</ruby>`
- Fix the component/fragment so `<rb>`/`<rtc>` are always rendered inside a ruby element
- When parsing fragments, give the parser the correct context element so scoping matches the final document
- Lint for rb/rt/rp/rtc tags that lack an ancestor ruby
Example fix
<!-- before --> <rb>漢</rb><rt>かん</rt> <!-- after --> <ruby><rb>漢</rb><rt>かん</rt></ruby>
Defensive patterns
Strategy: validation
Validate before calling
// Ensure rb/rtc tokens appear only inside a <ruby> wrapper
fn ruby_tags_orphaned(src: &str) -> bool {
let mut ruby = 0i32;
for tag in html_tag_tokens(src) {
match tag {
Start(ref n) if n == "ruby" => ruby += 1,
End(ref n) if n == "ruby" => ruby = ruby.saturating_sub(1),
Start(ref n) if matches!(n.as_str(), "rb"|"rtc") && ruby == 0 => return true,
_ => {}
}
}
false
} Type guard
fn is_rb_outside_ruby(e: &Error) -> bool {
matches!(
e.kind,
ErrorKind::StartTagSeenWithoutRuby(ref t) if matches!(&**t, "rb"|"rtc")
)
} Try / catch
let doc = parser.parse_document()?;
if parser.take_errors().into_iter().any(is_rb_outside_ruby) {
return Err(ContentRejected::RubyMarkupMissingWrapper);
} Prevention
- Always emit <ruby> before rb/rtc/rp/rt in renderers
- Render ruby annotation as one atomic component, not loose fragments
- Pass a correct context element when parsing innerHTML fragments of ruby markup
When it happens
Trigger: `<rb>` or `<rtc>` appearing outside any `<ruby>`: e.g. `<p><rb>漢</rb></p>`, or ruby markup assembled from separate strings/fragments where the `<ruby>` wrapper is missing or was closed too early.
Common situations: Building CJK furigana/annotation markup programmatically (dictionaries, language-learning apps) and forgetting the `<ruby>` wrapper; parsing innerHTML fragments that were extracted from a ruby context; template partials that render only the `rb`/`rt` pieces.
Related errors
- Unclosed children in "ruby"
- failed to parse input as document fragment
- failed to parse input as document
- failed to parse input as document fragment
- failed to parse input as document
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/6d5a287ffb06d9fe.
Report an issue: GitHub.