Describe the bug
On the proof-of-work challenge page, the client-side script (main.mjs) can throw an uncaught TypeError when it tries to access the #progress element:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'style')
at main.ts:159:12
Line 159 corresponds to:
progress.style.display = "inline-block";
with progress obtained via:
const progress: HTMLDivElement = g("progress") as HTMLDivElement;
Likely root cause
In the generated challenge HTML, the progress div is located after the module script tag in documnet order:
<script id="anubis-main" async type="module" src="/.within.website/x/cmd/anubis/static/js/main.mjs?..."></script>
<script>...bootstrap/retry logic...</script>
<div id="progress" role="progressbar" aria-labelledby="status">...</div>
A <script type="module"> without async behaves like defer and waits for the document to finish parsing. With async set, the module executes as soon as it's fetched, regardless of whether the rest of the document (including progress) has been parsed yet. If main.mjs loads fast enough, the script can run before progress exists in the DOM, causing getElementById("progress") to fail.
This could explain why it's browser-dependent. Chrome's module fetch/execution timing made the race condition reproducible consistently in our setup, while Firefox and Safari don't trigger it.
Steps to reproduce
- Run Anubis natively with the reverse proxy on the same host as Anubis.
- Load the challenge page in Chrome or Chrome Canary
Expected behavior
It should just work in Chrome. :D
Your operating system and its version.
MacOS 26
Your browser and its version.
Chrome 151.0.7922.72
Additional context
- Anubis version: v1.26.2 deb install
- OS: Debian 7 Wheezy. I'm aware this is far past EOL. It's a legacy client server I cannot migrate right now for budget reasons. Aside from this issue, Anubis itself runs completely fine.
- Reverse proxy: Apache 2.2, mod_proxy, Anubis on 127.0.0.1
- Affected browser: Chrome 151.0.7922.72 (macOS, arm64) and Chrome Canary, reproducible on every page load/reload
- Not affected: Firefox and Safari on the same machine/network load the same page without error
Workaround
We worked around this at the reverse-proxy level by stripping the async attribute using mod_substitute
RequestHeader unset Accept-Encoding
<Location "/">
AddOutputFilterByType SUBSTITUTE text/html
Substitute "s|<script id=\"anubis-main\" async|<script id=\"anubis-main\"|"
</Location>
This removes the async attribute so the module script behaves like defer and the error no longer occurs.
Describe the bug
On the proof-of-work challenge page, the client-side script (
main.mjs) can throw an uncaughtTypeErrorwhen it tries to access the#progresselement:Line 159 corresponds to:
with
progressobtained via:Likely root cause
In the generated challenge HTML, the progress div is located after the module script tag in documnet order:
A <script type="module"> without async behaves like defer and waits for the document to finish parsing. With
asyncset, the module executes as soon as it's fetched, regardless of whether the rest of the document (including progress) has been parsed yet. If main.mjs loads fast enough, the script can run before progress exists in the DOM, causing getElementById("progress") to fail.This could explain why it's browser-dependent. Chrome's module fetch/execution timing made the race condition reproducible consistently in our setup, while Firefox and Safari don't trigger it.
Steps to reproduce
Expected behavior
It should just work in Chrome. :D
Your operating system and its version.
MacOS 26
Your browser and its version.
Chrome 151.0.7922.72
Additional context
Workaround
We worked around this at the reverse-proxy level by stripping the async attribute using mod_substitute
This removes the async attribute so the module script behaves like defer and the error no longer occurs.