#23071: fix(tts): make config auto=off a hard-disable that overrides user prefs
size: XS
Cluster:
Security Enhancements for TTS
Fixes #22871.
`messages.tts.auto: "off"` in config was silently ignored when the user had previously enabled TTS via `/tts on`. The `/tts on` command writes `{"tts":{"auto":"always"}}` to the prefs file (`~/.openclaw/settings/tts.json`). Since `resolveTtsAutoMode` checks the prefs file *before* falling back to config, the prefs value always won — making the config setting useless in this case.
The workaround in the issue report (creating the prefs file manually with `auto: "off"`) worked for exactly this reason: the prefs file check came first.
## Root cause
`resolveTtsAutoMode` priority order was:
1. Session override (`/tts on` within a session)
2. Prefs file (`/tts on` persisted)
3. Config (`messages.tts.auto`)
Config ended up lowest priority, so an operator setting `auto: "off"` had no effect if the user had ever run `/tts on`.
## Fix
One check added at the top of `resolveTtsAutoMode`: if `config.auto === "off"`, return immediately. This makes the config setting an administrative hard-disable — consistent with how other operator-level flags work (e.g. `exec.enabled: false` cannot be overridden by users).
## Tests
Four new tests in `describe("resolveTtsAutoMode")`:
- Config `always` with no prefs returns `always` (existing behavior unchanged)
- Config `off` overrides prefs file set to `always` (reproduces #22871)
- Config `off` overrides session-level `ttsAuto`
- Prefs file still wins over config when config is not `off`
Closes #22871
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Changed the priority order in `resolveTtsAutoMode` to make `config.auto === "off"` an administrative hard-disable that immediately returns `off`, overriding both session-level and prefs-file settings. Previously, operator-level config was lowest priority and could be overridden by user preferences, making the config setting ineffective when users had enabled TTS via `/tts on`. The fix aligns with how other operator-level flags work (e.g., `exec.enabled: false`).
- Added early return check for `config.auto === "off"` at the top of `resolveTtsAutoMode` (src/tts/tts.ts:339-342)
- Added four new tests covering the new priority behavior (src/tts/tts.test.ts:423-461)
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The change is minimal (4 lines of production code), well-tested with comprehensive test coverage for all scenarios, and the logic is straightforward. The fix correctly addresses the reported issue by making operator config take precedence over user prefs when set to `off`, which aligns with the principle of least surprise for administrative controls.
- No files require special attention
<sub>Last reviewed commit: 5f4ebfa</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23291: fix(tts): respect config auto=off over user prefs override
by SidQin-cyber · 2026-02-22
90.7%
#22086: fix(tts): honor explicit config provider and model/voice settings
by AIflow-Labs · 2026-02-20
82.4%
#11109: fix(tui): prefer config contextTokens over persisted session value
by marezgui · 2026-02-07
77.5%
#6677: fix(tts): always load fresh config for voice selection
by Jinqiao · 2026-02-01
77.2%
#18193: fix: default elevatedDefault to 'off' instead of 'on' (#18177)
by lailoo · 2026-02-16
76.8%
#19412: fix(status): prefer configured contextTokens over session entry
by rafaelipuente · 2026-02-17
75.7%
#19624: fix: elevatedDefault should default to off when tools.elevated.enab...
by stakeswky · 2026-02-18
74.9%
#16089: fix(tts): clarify directive syntax in prompts and strip malformed tags
by kmixter · 2026-02-14
74.6%
#19684: fix: change elevatedDefault fallback from 'on' to 'off'
by neipor · 2026-02-18
74.0%
#4109: fix(cli): bypass config guard for tui --url
by hopenjin · 2026-01-29
73.6%