fix: validate settings before stopping daemon on restart#219
fix: validate settings before stopping daemon on restart#219jcenters wants to merge 1 commit intoTinyAGI:mainfrom
Conversation
If settings.json is broken or missing channels.enabled, restart_daemon would kill the live session then fail to start a new one, leaving nothing running. Add preflight_check() to validate settings upfront and abort the restart if they're invalid, preserving the live session. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Greptile SummaryThis PR fixes a race condition in Key changes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
actor User
participant restart_daemon
participant preflight_check
participant load_settings
participant stop_daemon
participant start_daemon
User->>restart_daemon: tinyclaw restart
restart_daemon->>preflight_check: preflight_check()
preflight_check->>load_settings: load_settings()
load_settings-->>preflight_check: rc=0 (ok) / rc=1 (missing) / rc=2 (bad JSON)
alt rc=2 (invalid JSON)
preflight_check-->>restart_daemon: return 1
restart_daemon-->>User: ❌ Aborted — live session preserved
else rc≠0 (missing/no config)
preflight_check-->>restart_daemon: return 1
restart_daemon-->>User: ❌ Aborted — live session preserved
else ACTIVE_CHANNELS empty
preflight_check-->>restart_daemon: return 1
restart_daemon-->>User: ❌ Aborted — live session preserved
else token missing for a channel
preflight_check-->>restart_daemon: return 1
restart_daemon-->>User: ❌ Aborted — live session preserved
else all checks pass
preflight_check-->>restart_daemon: return 0
restart_daemon->>stop_daemon: stop_daemon()
stop_daemon-->>restart_daemon: done
restart_daemon->>start_daemon: start_daemon()
start_daemon-->>restart_daemon: done
restart_daemon-->>User: ✅ Daemon restarted
end
Last reviewed commit: b8169c1 |
| # Returns 0 if ready to start, non-zero otherwise. | ||
| # Safe to call while the daemon is running — reads only, no side effects. |
There was a problem hiding this comment.
Misleading "no side effects" comment
The comment claims preflight_check is "reads only, no side effects", but load_settings() (called on line 274) actually modifies several global variables: ACTIVE_CHANNELS, WORKSPACE_PATH, _CHANNEL_TOKEN_KEYS, and _CHANNEL_TOKEN_VALS.
This is currently benign — start_daemon() calls load_settings() again immediately after and reinitialises these globals — but the comment could mislead a future developer into calling preflight_check() from a context where those globals must remain untouched.
| # Returns 0 if ready to start, non-zero otherwise. | |
| # Safe to call while the daemon is running — reads only, no side effects. | |
| # Validate settings without starting the daemon. | |
| # Returns 0 if ready to start, non-zero otherwise. | |
| # NOTE: calls load_settings() internally, which resets ACTIVE_CHANNELS, | |
| # WORKSPACE_PATH, and the channel-token arrays as a side effect. | |
| preflight_check() { |
| if [ $load_rc -eq 2 ]; then | ||
| local jq_err | ||
| jq_err=$(jq empty "$SETTINGS_FILE" 2>&1) | ||
| echo -e "${RED}Restart aborted: settings.json contains invalid JSON${NC}" | ||
| echo -e " ${YELLOW}${jq_err}${NC}" | ||
| echo " Fix manually: $SETTINGS_FILE" | ||
| return 1 |
There was a problem hiding this comment.
Auto-repair path bypassed and error message is incomplete
start_daemon() (lines 39-66) has an auto-repair step: when load_rc -eq 2, it invokes jsonrepair to attempt to fix the broken JSON before giving up. preflight_check skips that step entirely and tells the user to "Fix manually", which is accurate but incomplete — it doesn't inform the user that tinyclaw stop && tinyclaw start would attempt automatic repair.
Consider either:
- Mentioning the auto-repair path in the error message:
| if [ $load_rc -eq 2 ]; then | |
| local jq_err | |
| jq_err=$(jq empty "$SETTINGS_FILE" 2>&1) | |
| echo -e "${RED}Restart aborted: settings.json contains invalid JSON${NC}" | |
| echo -e " ${YELLOW}${jq_err}${NC}" | |
| echo " Fix manually: $SETTINGS_FILE" | |
| return 1 | |
| if [ $load_rc -eq 2 ]; then | |
| local jq_err | |
| jq_err=$(jq empty "$SETTINGS_FILE" 2>&1) | |
| echo -e "${RED}Restart aborted: settings.json contains invalid JSON${NC}" | |
| echo -e " ${YELLOW}${jq_err}${NC}" | |
| echo " Fix manually: $SETTINGS_FILE" | |
| echo " Or run 'tinyclaw stop && tinyclaw start' to attempt auto-repair" | |
| return 1 |
- Documenting in the function comment that auto-repair is intentionally skipped to avoid side effects during preflight.
Bug
restart_daemon()calledstop_daemonbefore verifying the new settings are valid. Ifsettings.jsonis broken (e.g. missingchannels.enabled), the old session was already dead andstart_daemonfailed silently — leaving nothing running.Both restart paths were broken the same way:
Normal (outside tmux):
Inside tmux:
Fix
Added a
preflight_check()function that runs the settings validation (load_settings+ token check + channels check) without starting or writing anything. It is called at the top ofrestart_daemonbefore any stop action. If it fails, the restart is aborted with a clear error message — the live session stays up.settings.json: aborts with the jq parse error and path to fix ittinyclaw setuptinyclaw setuptinyclaw setupTest plan
settings.json,tinyclaw restartworks as beforesettings.json,tinyclaw restartprints the parse error and exits without killing the running sessionchannels.enabledremoved fromsettings.json,tinyclaw restartaborts with "no channels configured" and the daemon keeps runningtinyclaw restartidentifies the affected channel and aborts🤖 Generated with Claude Code