Skip to content

Failed network fetches never call back, leaving critical resources pending forever #694

Description

Summary

When a network fetch fails (HTTP error status, IO error, or abort), the NetHandler for the request is silently dropped without any callback. If the request was a render-blocking ("critical") resource such as a <link rel=stylesheet> in <head>, its request id is never removed from BaseDocument::pending_critical_resources, so has_pending_critical_resources() returns true forever and painting is permanently blocked (paint_scene early-returns, and blitz-shell skips rendering).

Details

  • NetHandler only has a success path:

    pub trait NetHandler: Send + Sync + 'static {
        fn bytes(self: Box<Self>, resolved_url: String, bytes: Bytes);
    }

    (packages/blitz-traits/src/net.rs)

  • blitz-net's Provider::fetch logs errors and drops the handler without invoking anything:

    match result {
        Ok((response_url, bytes)) => { handler.bytes(response_url, bytes); ... }
        Err(e) => {
            tracing::error!(url = url.as_str(), error = ?e, "Error fetching");
        }
    };

    (packages/blitz-net/src/lib.rs)

  • The unblocking path is Document::load_resource, which removes the request id from pending_critical_resources before checking success/failure — so failures that are delivered as Err through ResourceHandler::respond (e.g. invalid UTF-8 CSS) unblock correctly. Only failures at the net-provider level never reach it.

Browsers treat a failed render-blocking stylesheet as "loaded with zero rules" and unblock rendering.

Suggested fix

  1. Give NetHandler an explicit failure path (e.g. fn error(self: Box<Self>, err: String) with a default no-op, or change bytes to take a Result), and make blitz-net (and other providers) deliver failures.
  2. Belt-and-braces: a Drop impl on ResourceHandler that sends an Err ResourceLoadResponse if the handler was dropped without ever responding — this also covers third-party NetProvider implementations that drop handlers without calling anything (including the abort path).
  3. Optionally, a timeout on render-blocking so a hung (never-completing) fetch cannot blank the page forever.

Related: this failure mode becomes more impactful with the proposed fix for #689 (deferring style resolution, not just painting, while critical resources are pending).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions