Skip to content

UrlLib: Win32/UWP UrlRequest silently succeeds on local-file-not-found (inconsistent with Apple/Android) #167

@bghgary

Description

@bghgary

Filed here because BabylonJS/UrlLib has issues disabled; this issue is about the bundled UrlLib dependency. Move once that repo's issues are enabled, if desired.

Summary

UrlLib's UrlRequest reports local-file-not-found differently across platforms. Two platforms throw; two silently return success with a zero status code.

Platform Source (in BabylonJS/UrlLib) Local file not found HTTP non-2xx
Win32 UrlRequest_Win32.cpp (via UrlRequest_Windows_Shared.h) success-task, m_statusCode = 0 success-task, real status code
UWP UrlRequest_UWP.cpp (via UrlRequest_Windows_Shared.h) success-task, m_statusCode = 0 success-task, real status code
Apple UrlRequest_Apple.mm throws ("No file exists at local path") success-task, real status code
Android UrlRequest_Android.cpp throws ("Failed to open asset") success-task, real status code
Unix UrlRequest_Unix.cpp (no local-file path; CURL handles) success-task, real status code

HTTP non-2xx behavior is consistent (good — that's the W3C-compliant shape: transport succeeded, status code carries the outcome).

Local-file behavior is the bug — callers can't distinguish "file was loaded fine, server returned 0 bytes" from "file does not exist" on Windows family, without falling back to status-code heuristics.

Impact

#165 works around this in the XHR polyfill by treating any status < 200 || status >= 300 as a failure in the error event path. That's a defensible XHR-level decision (XHR has to check status code anyway for HTTP errors), but it papers over the platform inconsistency rather than fixing it. With this workaround in place, all callers via XMLHttpRequest see consistent behavior, but direct UrlLib callers still see the inconsistency.

Proposed fix

Two options, in decreasing order of preference:

  1. Make Win32/UWP throw on local-file-not-found, matching Apple/Android. Callers can catch via arcana::expected::has_error(). This is the most internally consistent behavior and gives callers a clean way to distinguish transport failure from HTTP failure.

  2. Alternatively, standardize on success-task + a synthetic status code (e.g., 404 for missing local file) across all platforms. Less invasive for existing callers, but conflates HTTP and transport semantics.

Either option requires updating Apple/Android in lockstep so all five paths agree.

Repro

const xhr = new XMLHttpRequest();
xhr.addEventListener("error", () => console.log("error fired"));
xhr.addEventListener("loadend", () => console.log("loadend; status=", xhr.status));
xhr.open("GET", "app:///Scripts/this_file_does_not_exist.js");
xhr.send();

Apple/Android (before #165): error fires.
Win32/UWP (before #165): only loadend fires; xhr.status is 0.

[Filed by Copilot on behalf of @bghgary]

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