DioxusLabs/dioxus · error
Failed to parse index.html from public directory
Error message
Failed to parse index.html from public directory
What it means
Thrown by the fullstack server during ServeConfig::new() when a public/index.html exists but IndexHtml::new cannot parse it. The parser is a naive string splitter that requires a literal id="main" attribute (the hardcoded root id), a > closing that tag, a </head> tag before the root element, a </body> tag after it, and a closed <title> pair. Any missing marker makes IndexHtml::new return Err, which config.rs unwraps with expect, aborting server startup.
Source
Thrown at packages/fullstack-server/src/config.rs:64
}
/// Create a new ServeConfig with incremental static generation disabled and the default index.html settings
///
/// This will automatically use the `index.html` file in the `/public` directory if it exists.
/// The `/public` folder is meant located next to the current executable. If no `index.html` file is found,
/// a default index.html will be used, which will not include any JavaScript or WASM initialization code.
///
/// To provide an alternate `index.html`, you can use `with_index_html` method instead.
pub fn new() -> Self {
let index = if let Some(public_path) = crate::public_path() {
let index_html_path = public_path.join("index.html");
if index_html_path.exists() {
let index_html = std::fs::read_to_string(index_html_path)
.expect("Failed to read index.html from public directory");
IndexHtml::new(&index_html, "main")
.expect("Failed to parse index.html from public directory")
} else {
IndexHtml::ssr_only()
}
} else {
tracing::warn!(
"Cannot identify public directory, using default index.html. If you need client-side scripts (like JS + WASM), please provide an explicit public directory."
);
IndexHtml::ssr_only()
};
Self {
index,
incremental: None,
context_providers: Default::default(),
streaming_mode: StreamingMode::default(),
}
}
View on GitHub (pinned to 393d190a80)
Solutions
- Add <div id="main"></div> inside <body> as the app root container
- Ensure a literal </head> appears before the root div and a literal </body> appears after it
- Close the title element: <title>...</title>
- If you don't need a custom shell, remove/rename index.html so the server falls back to IndexHtml::ssr_only()
- If you need a different root id, pass a valid document via ServeConfig::with_index_html instead
Example fix
// before (public/index.html) <html><head><title>App</title><body><div id="root"></div></body></html> // after <html><head><title>App</title></head><body><div id="main"></div></body></html>
Defensive patterns
Strategy: validation
Validate before calling
fn index_html_is_parsable(public_dir: &std::path::Path) -> bool {
let Ok(html) = std::fs::read_to_string(public_dir.join("index.html")) else {
return false; // server falls back to ssr_only
};
let (_, post_main) = html.split_once("id=\"main\"").unwrap_or(("", "NONEMAIN"));
html.split_once("id=\"main\"").is_some()
&& post_main.split_once('>').is_some()
&& html.split_once("</head>").is_some()
&& html.split_once("</body>").is_some()
&& !(html.contains("<title>") && !html.contains("</title>"))
}
// assert!(index_html_is_parsable(Path::new("public"))) before building the server Prevention
- Keep a CI check that validates public/index.html contains id=\"main\", </head> and </body>
- Treat the default generated index.html as the template and only add elements inside it
- If you change the root id in Dioxus.toml, remember the fullstack server path still expects id=\"main\" unless you use with_index_html
When it happens
Trigger: Launching a fullstack app (dioxus::launch / LaunchBuilder with a server) where <crate>/public/index.html exists but: the root element has a different id (id="root", id="app"), </head> or </body> tags were removed/reordered, the id="main" attribute is not followed by >, or a <title> tag is unclosed.
Common situations: Porting an index.html from a Vite/React template that uses id="root"; hand-minified HTML with dropped closing tags; changing the root id in Dioxus.toml while the fullstack server still looks for id="main"; malformed HTML from a custom build step.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Head element to exist
- Body element to exist
- Failed to read index.html from public directory
- Lazy value is not initialized. Make sure to call `initialize
- Failed to create new router after hot-patch!
AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16).
Data as JSON: /api/errors/e6125e0fcfa8eb5c.
Report an issue: GitHub.