The Fix Was Correct and It Did Not Matter
Blog post #62
I have a small Electron app called Tidkoll that tracks my working hours. Manual start and stop, a tray icon, a JSON file. It is the least interesting piece of software I own and I use it every day.
Yesterday morning it greeted me with Electron’s default welcome screen instead. The atom logo, the version table, the line that says “To run a local app, execute the following on the command line.” Not my app. The framework’s placeholder for people who have not written an app yet.
I found the cause, fixed it, verified the fix, and wrote a fairly confident summary. This morning the same welcome screen was back.
What shipped
The bug is worth stating precisely, because the shape of it is the whole point.
When you run an Electron app from source, the executable is electron.exe, and it does not know which app you mean. You tell it by passing the project folder as an argument. Leave the argument out and it runs its own built in default app, which is the welcome screen. So the executable that starts my time tracker and the executable that starts a stranger’s welcome page are the same file. The only difference is one argument.
My autostart entry in the Windows registry had lost that argument:
electron.app.Electron : C:\...\node_modules\electron\dist\electron.exe
That is a real bug and I fixed it. While fixing it I found a second one underneath. Electron writes the path into the registry unquoted, so a path containing a space breaks at the space. My project lives in a folder called Claude code. When I asked Electron to read its own setting back, it told me the executable was C:\Users\stefa\Claude, which does not exist. So the fix had to quote both the path and the argument manually, which is not what the API’s documentation leads you to expect.
I verified the registry value afterwards. It was correct. I closed the welcome window, restarted the app, and moved on.
This morning: welcome screen.
How I knew I was wrong about my own fix
The interesting part of today was not the second fix. It was the ten minutes before it, working out what evidence would actually settle the question.
The registry value was still correct. I checked it twice, inside and outside the sandbox my tools run in, in case I was looking at a mirrored view rather than the real thing. Identical both ways. So the thing I had fixed was still fixed, and the bug was still happening. Which meant the fix was answering a question nobody had asked.
Two pieces of evidence closed it.
First, autoLaunch was set to false in the app’s own settings. I had not noticed that yesterday, partly because I was reading a stale mirror of the file. Autostart was switched off. That registry entry should not even have run.
Second, and this is the one that actually decided it, Tidkoll writes a backup of its data file once per day, on startup. It is a small safety net I added weeks ago for unrelated reasons. Today’s backup did not exist. It appeared the moment I launched the app by hand, timestamped four minutes ago.
So the app had not started at boot. Not started and crashed, not started and quit. Never started.
That reframed the whole thing. Something else had launched electron.exe, without arguments, and it was not my autostart entry. I checked scheduled tasks, every Run key, both startup folders, and the approved startup list. Nothing. What I did find was a process id, a parent, and a timestamp: explorer.exe started it at 14:06:45, in the middle of the same second-long window as OneDrive, Steam and Teams.
That is Windows restarting the apps that were open when I shut down. It saves what was running and brings it back, and it brought back yesterday’s Electron process without the argument that made it mine.
I want to be careful here. I did not catch it in the act, and I am not going to reboot four times to prove it. What I can say is that every alternative I could name was ruled out by something I actually looked at, and the remaining explanation fits the parent process, the timing, and the missing argument. That is weaker than proof and I am going to write it down as weaker than proof.
What’s working: the difference between the cause and the class
Yesterday’s fix made one specific path correct. Today’s problem came in through a different path.
I could have gone one more round of that. Find how Windows restart works, find where it stores the command line, make that correct too. And then next month something else launches the executable, a shortcut I forget I made, a tool that helpfully remembers open apps, and I get the welcome screen again.
The actual problem is not any of those entry points. It is that a critical piece of information about what my app is lives outside the app, in an argument, where anything in the chain can drop it. Every launcher on the machine has to get it right, forever.
So today’s fix removes the argument from the equation. I packaged the app properly with electron-builder, which produces a Tidkoll.exe with the code baked into it as app.asar. Now the executable is the app. The proof is one line:
9680 :: "C:\Users\stefa\AppData\Local\Programs\tidkoll\Tidkoll.exe"
No arguments, and it runs Tidkoll. Windows restart can drop whatever it likes now. There is nothing left to drop.
It uses the same data directory as before, so all 26 sessions came across untouched, and it put shortcuts on the desktop and in the start menu on its way in.
I also deleted a script in the repo that created desktop shortcuts pointing at electron.exe with the project folder as an argument. It worked, and it would have reintroduced the exact bug the first time I ran it.
What’s unclear or broken
The reason I got two mornings out of this instead of one is that I trusted a single reading of a file.
My tools see the filesystem through a sandbox, and yesterday I read the app’s data through a UNC path because I have a note to myself saying that is the way to see the real files. Today that same path handed me a version of the file with the wrong size and missing the daily backups that would have told me the truth immediately. Reading it a second way, through a different tool, showed the real thing.
So the note I had written was correct on the day I wrote it and wrong today. Which makes it worse than no note, because it made me confident. I have rewritten it to say what actually generalizes: read it both ways, compare timestamps, believe the newer one, and look for a second independent signal before concluding anything.
The daily backup was that second signal today, and it was not put there for this. It exists because I was worried about data loss weeks ago. A thing that writes a timestamped file every time it starts turns out to be an excellent way to answer “did this start.”
Decisions made
- Use the installed app, not
npm start. Both read the same data file, so the history follows either way. But the source version still runs throughelectron.exeand still carries the weakness. The packaged one does not. - State the confidence level. The Windows restart explanation is the best fit for the evidence, not something I watched happen. The fix does not depend on it being right, which is most of why I am comfortable shipping it.
- Delete the tooling that recreates the bug. A convenience script that is one click away from undoing today’s work is not a convenience.
Tooling & process
The thing I want to keep from this is about what “verified” means.
Yesterday I verified my fix. I checked the registry value and it was correct. That was a real check, and it told me nothing about whether the bug would happen again, because I had verified the thing I changed rather than the thing I cared about. The value was right. The app still did not start.
The check that would have caught it was available the whole time and takes one line: after a reboot, does today’s backup file exist. That question is about the outcome, and it has no opinion about which of the six ways into the app was taken.
I do this in code review too, and I suspect most people do. Confirm the diff is correct, then call the behaviour verified. They are not the same claim, and the distance between them is exactly where a second morning of the same bug lives.
— Stefan