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

  1. Add <div id="main"></div> inside <body> as the app root container
  2. Ensure a literal </head> appears before the root div and a literal </body> appears after it
  3. Close the title element: <title>...</title>
  4. If you don't need a custom shell, remove/rename index.html so the server falls back to IndexHtml::ssr_only()
  5. 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

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

Related errors


AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16). Data as JSON: /api/errors/e6125e0fcfa8eb5c. Report an issue: GitHub.