Tor Browser had worked fine the day before. The next morning it refused to start, and the dialog it gave me was the kind that tells you nothing at all.
Tor exited during startup. This might be due to an error in your torrc file, a bug in Tor or another program on your system, or faulty hardware. Until you fix the underlying problem and restart Tor, Tor Browser will not start.
Three possible causes, no way to tell which one. I could reach the Connect to Tor screen, but Try Again did nothing and the bootstrap never moved off zero. The in-app log viewer, the one place you would expect an answer, was completely empty.
Nothing had changed. No update, no config edit, no new software. Every other device on the same router could use Tor without trouble, so it was not the ISP and it was not the network. Whatever was wrong was local to this one machine, and it had appeared overnight.
An empty log is itself a clue
The empty log turned out to be the first useful piece of information rather than a dead end. Tor never got far enough to open a log destination, which means the failure happened during startup, before anything could be written. The GUI showed nothing because there was nothing yet to show.
So I ran the binary myself, from a command prompt, where output has nowhere to hide.
cd "C:\Users\<user>\Desktop\Tor Browser\Browser\TorBrowser\Tor"
tor.exe
The real error was there immediately.
[notice] Opening Socks listener on 127.0.0.1:9050
[warn] Could not bind to 127.0.0.1:9050: Permission denied [WSAEACCES]
[warn] Failed to parse/validate config: Failed to bind one of the listener ports.
[err] Reading config failed--see warnings above.
WSAEACCES on a loopback bind is the decisive detail. It reads like a file permissions problem and it is not one. On Windows it means something else already owns that port, and that something is usually the operating system itself.
Windows had reserved the entire 9000 range
Windows keeps a list of TCP port ranges it has set aside. You can read it directly.
netsh int ipv4 show excludedportrange protocol=tcp
The answer was sitting in the output.
Start Port End Port
---------- --------
2559 2658
8993 9092 <-- contains 9050
9093 9192 <-- contains 9150 and 9151
9193 9292
9293 9392
9393 9492
9493 9592
9593 9692
9693 9792
10089 10188
50000 50059 *
Tor needs 9050 and 9051 for the standalone binary, and 9150 and 9151 for Tor Browser. Every one of them was inside a reserved block. The listener could not bind, so the process aborted before it did anything else.
Why it broke overnight
The culprit is winnat, the NAT service behind Hyper-V, WSL2 and Docker Desktop. It claims a set of dynamic port ranges at every boot, and the ranges it picks are not the same each time. Most mornings it lands somewhere harmless. On this particular boot it happened to swallow the block Tor needed.
That explains the part that made no sense: it worked yesterday, it failed today, and nothing changed. Nothing did change. The dice were simply rolled again at startup.
What did not work
The failed attempts are worth listing, because each one is the obvious thing to try and each one wastes time on the wrong layer.
- Restarting Tor, rebooting the machine. Sometimes appears to work, which is worse than failing. The ranges are re-randomised at every boot, so the conflict comes back whenever the dice land badly again.
- Turning off the VPN, switching servers. Irrelevant. The failure happened locally, before a single packet left the machine.
- Built-in bridges, obfs4, snowflake. Irrelevant for the same reason. Bridges solve censorship. This was not censorship.
- Antivirus and Defender quarantine. A genuinely common cause of these symptoms, but nothing had been blocked here.
- Reinstalling Tor Browser. The installation was not damaged, so this would have changed nothing.
One attempt did work, partly, and it is instructive. Moving Tor to unused ports in torrc:
SocksPort 9850
ControlPort 9851
Passing that file to the binary explicitly did fix the standalone process, and bootstrap ran normally past fifty percent:
tor.exe -f "...\Browser\TorBrowser\Data\Tor\torrc"
But launching Tor Browser from its icon still failed, because the browser decides its own ports at launch and does not honour those entries. So the fix proved the diagnosis while solving nothing, which is a useful thing for a fix to do. I removed those lines afterwards.
A few things I inspected along the way looked suspicious and were entirely normal: torrc being zero bytes, since it is only a user override file and ships empty; torrc-defaults being present; torrc.orig being absent, as it is only created on rewrite; and warnings that the GeoIPFile path is relative.
The fix
The instinct is to move Tor to a free port. The better move is the opposite: claim the ports Tor Browser actually wants, permanently, so Windows can never take them again.
In an elevated command prompt:
net stop winnat
netsh int ipv4 add excludedportrange protocol=tcp startport=9150 numberofports=2 store=persistent
net start winnat
Stopping winnat releases the dynamic ranges it is holding. The netsh line reserves 9150 and 9151 as an administered exclusion, and store=persistent is what makes it survive reboots. Restarting the service lets it allocate around the reservation instead of over it.
If net stop winnat errors, the service is not installed and you can skip that line. If netsh reports access denied, reboot and run the commands immediately after login, before anything else claims ports.
Verifying it
netsh int ipv4 show excludedportrange protocol=tcp
Start Port End Port
---------- --------
9150 9151 *
50000 50059 *
* - Administered port exclusions.
Two things confirm it. The Tor ports now appear with an asterisk, meaning administered rather than dynamic. And the large blocks spanning 8993 to 9792 are gone entirely, released when the service stopped. Tor Browser opened from its desktop icon and connected without further argument.
What I would take from this
The specific fix is narrow. The habits behind it are not.
- When a GUI fails silently, run its executable from a terminal. The real error is almost always printed there, and an empty in-app log usually means the process died before logging existed.
- On Windows,
WSAEACCESon a 127.0.0.1 bind means the OS took the port. It is not a permissions problem, whatever the wording suggests. Check withnetsh int ipv4 show excludedportrange protocol=tcpbefore assuming anything else. - “It worked yesterday and nothing changed” is a signature, not a mystery. On machines running Hyper-V, WSL2 or Docker Desktop, it points hard at boot-time dynamic port reservation.
- If other devices on the same network work, stop investigating the network. I would have found this faster if I had trusted that earlier instead of touching the VPN.
- Prefer the durable fix over the one that appears to work. Rebooting re-rolls the dice. Reserving the port with
store=persistentends the problem.
I have not checked whether this is documented anywhere in the Tor community. I worked it out on this machine, from the symptoms up, and I am writing it down in case it saves someone else the morning it cost me.
If this kind of problem sounds familiar
Most of the hard bugs are one layer below where everyone is looking.
This one wasted a morning on the network, the VPN and the antivirus before the answer turned out to be the operating system quietly taking a port. If something in your stack is failing for reasons nobody can name, that is the kind of work I enjoy most.