Skip to main content
Version: 3.1.0

Tower Restart Loop on Daily Purge Cron + Cron Resilience

Version: 3.1.0 Date: 2026-06-12

Goal: Stop the nightly restart loop observed on Towers stuck in need_configuration. The 3 AM purge cron raised an unhandled rejection (ENOENT '/data/work') that killed the Node process. More broadly, several crons and FS code paths were fragile to a Tower that had never been configured.

Solution:

  • AppController.purge() is now async and awaits DataService.purgeWork() so rejections fall into the try/catch.
  • DataService.purgeWork() early-returns if workFolder does not exist; onModuleInit() creates workFolder (/data/work) at boot.
  • LoggerService uses mkdirSync(logDir, { recursive: true }).
  • loadState() and getTask() no longer rely on flag: 'a+' to create files on read — ENOENT is handled explicitly.
  • SourcesService.discover(), TaskService.run(), TaskService.moveWaitingTaskToQueued() early-return when the Tower has no config; purge stays scheduled but is now safe.
  • main.ts registers global unhandledRejection (log + swallow) and uncaughtException (log + clean exit) handlers; bootstrap() is awaited with a .catch.
  • Audit of LoggerService.error(msg, error) misuses: 8 FTP-side call sites passed an Error in the id: string parameter, fixed to (msg, null, error) (or task.uuid when available).

Bug Fixes:

  • Tower no longer restarts at 3 AM when not yet configuredpurge() awaits its async work, purgeWork tolerates a missing folder, workFolder created at boot.
  • Idle crons no longer run on unconfigured Towersdiscover, run, moveWaitingTaskToQueued gated by ConfigurationService.hasConfig().

Improvements:

  • Process-level error handlers + clean container restartunhandledRejection / uncaughtException handlers in main.ts.
  • LoggerService.error signature usage audit — 8 FTP-related call sites fixed to the canonical error(msg, null, error) signature.

Files Modified:

  • src/app.controller.ts - purge() async, awaits purgeWork(), logger signature corrected
  • src/core/data/data.service.ts - purgeWork() early-return; onModuleInit() creates workFolder; explicit ENOENT handling in loadState() / getTask()
  • src/protocol/source/source.service.ts - discover() early-returns when no config; correct error logging
  • src/core/task/task.service.ts - run() and moveWaitingTaskToQueued() early-return when no config
  • src/core/task/task.service.spec.ts - hasConfig setup + 2 "no config → no-op" tests
  • src/main.ts - registerProcessErrorHandlers(logger), bootstrap() wrapped with .catch
  • src/protocol/source/ftp/ftp.service.ts - error log signature
  • src/protocol/storage/ftp/ftp.storage.service.ts - task.uuid as id, e as data
  • src/protocol/capacities/ftp-capacity/ftp-capacity.service.ts - 6 sites fixed to (msg, null, e)
  • src/core/logger/logger.service.ts - mkdirSync uses { recursive: true }
  • src/core/initialization/initialization.service.ts - fixed typo + duplicated job name in log