actualbudget / actualbudget/actual
[Bug]: API: schedule service crashes when shutdown wins the race with its queued run
- Vorherrschende Sprache
- TypeScript
- Sterne
- 28.7k
- Forks
- 3k
- Ø Merge
- 2 T. 11 Std.
- Gemergte PRs (30 T.)
- 65
Beschreibung
### What happened?
I run @actual-app/api 26.8.1 from Node 22 to sync my budget on a schedule.
Every now and then the process dies after a sync with:
TypeError: Cannot read properties of null (reading 'prepare')
at runQuery$2 (dist/index.js:1103:39)
at all (dist/index.js:62569:9)
at aqlQuery$1 (dist/index.js:14948:9)
at advanceSchedulesService (dist/index.js:114129:36)
at _runMutator (dist/index.js:59907:9)
It's not a blocker because next run usually works, but it's been happening consistently
enough. I've asked Kimi Code to help me find the issue, and this was summary:
#5810 added the getDatabase() guard for this crash, but the guard runs in the
sync handler before the service is queued. runMutator is not awaited, so when
shutdown() closes the database first, the queued advanceSchedulesService runs
against a null db and the unhandled rejection kills the process.
Two details that make the hole concrete:
- shutdown() emits its own "sync" event, so the service can be queued during
shutdown itself.
- close-budget awaits flushRunningMethods(), but that set tracks only
non-mutator handlers; the mutator queue is never drained before the db closes.
Side effect worth fixing with it: lastScheduleRun is stamped before the run, so
a crashed service never retries that day. On a budget with due schedules that
silently skips a day of auto-posting.
### How can we reproduce the issue?
I've asked Kimi for short repro steps (challenge is that it happens rarely, but over time consistently):
1. In the budget's metadata.json, set lastScheduleRun to yesterday. This makes
the next sync queue the schedule service.
2. In Node: await api.init(...); await api.downloadBudget(syncId);
await api.shutdown().
3. Repeat a few times. When shutdown wins the race, the process crashes with
the TypeError above, after the "Closing budget" log line.
It is a timing race, so it does not fire every run. The author of #5810 saw it
roughly every 100 API runs. A possible fix: re-check getDatabase() inside
advanceSchedulesService, or drain the mutator queue before close-budget.
### Where are you hosting Actual?
Other
### What browsers are you seeing the problem on?
Other
### Operating System
Mac OSX
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.