DioxusLabs/dioxus · error
Body element to exist
Error message
Body element to exist
What it means
The desktop protocol injects its JS module loader immediately before `</body>`; find("</body>").expect("Body element to exist") panics when the served index.html has no exact closing body tag. Unlike the head check, this runs on every root request regardless of custom_head.
Source
Thrown at packages/desktop/src/protocol.rs:129
// If the request is for the root, we'll serve the index.html file.
if request.uri().path() != "/" {
return None;
}
// Load a custom index file if provided
let mut index = custom_index.unwrap_or_else(|| DEFAULT_INDEX.to_string());
// Insert a custom head if provided
// We look just for the closing head tag. If a user provided a custom index with weird syntax, this might fail
if let Some(head) = custom_head {
index.insert_str(index.find("</head>").expect("Head element to exist"), &head);
}
// Inject our module loader by looking for a body tag
// A failure mode here, obviously, is if the user provided a custom index without a body tag
// Might want to document this
index.insert_str(
index.find("</body>").expect("Body element to exist"),
&module_loader(root_name, headless, edit_state),
);
Response::builder()
.header("Content-Type", "text/html")
.header("Access-Control-Allow-Origin", "*")
.body(index.into())
.ok()
}
/// Construct the inline script that boots up the page and bridges the webview with rust code.
///
/// The arguments here:
/// - root_name: the root element (by Id) that we stream edits into
/// - headless: is this page being loaded but invisible? Important because not all windows are visible and the
/// interpreter can't connect until the window is ready.
/// - port: the port that the websocket server is listening on for edits
/// - webview_id: the id of the webview that we're loading this into. This is used to differentiate betweenView on GitHub (pinned to 393d190a80)
Solutions
- Include a complete HTML document with `<body>...</body>` in the custom index
- Ensure the literal lowercase `</body>` is present; the lookup is case-sensitive
- Remove the custom index to fall back to dioxus's built-in default index
Example fix
// before
.with_custom_index("<div id='main'></div>".into())
// after
.with_custom_index("<!DOCTYPE html><html><head></head><body><div id='main'></div></body></html>".into()) Defensive patterns
Strategy: validation
Validate before calling
fn index_has_body(index: &str) -> bool { index.contains("</body>") }
let html = std::fs::read_to_string("index.html").unwrap();
assert!(index_has_body(&html), "custom index must contain </body>"); Type guard
fn is_valid_custom_index(index: &str) -> bool {
index.contains("</body>") && index.contains("</head>")
} Prevention
- Never pass HTML fragments as with_custom_index - use a full document
- Smoke-test the app startup after every custom index change
- Remember both tag lookups are exact, case-sensitive string matches
When it happens
Trigger: Supplying a custom index via with_custom_index (or the configured index.html) that lacks `</body>` - e.g. only a fragment like `<div id="main"></div>` - or uses a case-mismatched `</BODY>`.
Common situations: Replacing index.html with a partial snippet; minified/templated HTML where the body tag got mangled; assuming the framework will wrap fragments automatically.
Related errors
- Head element to exist
- expected URL to be UTF-8 encoded
- tray icon builder failed
- Failed to parse index.html from public directory
- Failed to get asset manager
AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16).
Data as JSON: /api/errors/512cdb657280f25d.
Report an issue: GitHub.