yewstack/yew · error
failed to render application
Error message
failed to render application
What it means
ServerRenderer::render() runs the entire SSR pass on a spawned local task and receives the resulting String over a oneshot channel; this expect fires when rx.await returns Err, meaning the sender was dropped without sending. That happens only when the render task died - a panic inside component rendering, property creation, or a hook on the server side tore the task down before it could call tx.send(). The message is a symptom; the real error is the earlier panic in the server logs.
Source
Thrown at packages/yew/src/server_renderer.rs:262
create_props,
hydratable,
rt,
} = self;
let (tx, rx) = futures::channel::oneshot::channel();
let create_task = move || async move {
let props = create_props();
let s = LocalServerRenderer::<COMP>::with_props(props)
.hydratable(hydratable)
.render()
.await;
let _ = tx.send(s);
};
Self::spawn_rendering_task(rt, create_task);
rx.await.expect("failed to render application")
}
/// Renders Yew Application to a String.
pub async fn render_to_string(self, w: &mut String) {
let mut s = self.render_stream();
while let Some(m) = s.next().await {
w.push_str(&m);
}
}
#[inline]
fn spawn_rendering_task<F, Fut>(rt: Option<Runtime>, create_task: F)
where
F: 'static + Send + FnOnce() -> Fut,
Fut: Future<Output = ()> + 'static,
{
match rt {View on GitHub (pinned to 0e4a05472f)
Solutions
- Read the server logs immediately above this message - the panic that killed the render task is the actual error
- Guard all browser access with if yew::is_browser() { ... } or #[cfg(target_arch = "wasm32")] blocks
- Unit-test create_props and component view code compiled for the server target
- If using with_runtime, keep the runtime alive until render() completes
Example fix
// before: panics on the server - there is no window during SSR
fn view(&self, _ctx: &Context<Self>) -> Html {
let h = gloo::utils::window().inner_height();
// ...
}
// after: branch on environment
fn view(&self, _ctx: &Context<Self>) -> Html {
let h = if yew::is_browser() {
Some(gloo::utils::window().inner_height())
} else {
None
};
// ...
} Defensive patterns
Strategy: try-catch
Try / catch
// Turn the channel panic into a recoverable error and fall back to a CSR shell:
use futures::FutureExt;
use std::panic::AssertUnwindSafe;
let out = AssertUnwindSafe(renderer.render()).catch_unwind().await;
match out {
Ok(html) => response.body(html),
Err(_) => {
tracing::error!("SSR task panicked; see panic log above");
response.body(client_side_shell())
}
} Prevention
- Install a panic hook that logs the task panic - the expect here only reports the dead channel
- Guard every browser API call with yew::is_browser() or cfg(target_arch = "wasm32")
- Keep custom runtimes passed to with_runtime alive until render() completes
- Compile and run component tests against the server target in CI
When it happens
Trigger: Any panic during server rendering: components calling browser-only APIs (gloo::utils::window()/document(), web_sys access) without guards; expects/unwraps inside create_props or view code; a custom runtime passed via with_runtime being dropped before the render completes.
Common situations: Porting a CSR app to SSR without is_browser()/cfg guards; unwrapping request data when building props; custom tokio runtimes shut down early; effects or hooks that assume a browser environment.
Related errors
- resolver not set on server-side LinkProvider
- only structs are supported
- this hook takes 2 arguments but 1 argument was supplied
- this hook takes 2 arguments but 1 argument was supplied
- failed to clone node.
AI-assisted analysis of yewstack/yew@0e4a05472f (2026-08-22).
Data as JSON: /api/errors/0639e0544b35462d.
Report an issue: GitHub.