Skip to content

fix(gateway): config.yaml wins over .env for agent/display/timezone settings - #18764

Merged
teknium1 merged 1 commit into
mainfrom
fix/gateway-max-turns-bridge-silent-failure
May 2, 2026
Merged

fix(gateway): config.yaml wins over .env for agent/display/timezone settings#18764
teknium1 merged 1 commit into
mainfrom
fix/gateway-max-turns-bridge-silent-failure

Conversation

@teknium1

@teknium1 teknium1 commented May 2, 2026

Copy link
Copy Markdown
Contributor

Problem

Gateway silently capped at 60 tool-calling iterations per turn despite agent.max_turns: 500 in config.yaml. Telegram/Discord sessions hit _handle_max_iterations at exactly iteration 60 on every long turn. CLI (which reads agent.max_turns directly) was unaffected.

Root cause was a stale HERMES_MAX_ITERATIONS=60 entry left in .env by an old hermes setup run, combined with a not in os.environ guard pattern in the gateway's config→env bridge that let the stale .env value silently shadow the current config.yaml.

Bridge Inconsistency

The config→env bridge at gateway import time (gateway/run.py) was inconsistent across keys:

  • max_turns → unconditional overwrite (config wins) ✓
  • gateway_timeout, gateway_timeout_warning, gateway_notify_interval, restart_drain_timeout, gateway_auto_continue_freshness → guarded by if X not in os.environ (stale .env wins) ✗
  • display.busy_input_mode, display.busy_ack_enabled → guarded (stale .env wins) ✗
  • timezone → guarded (stale .env wins) ✗
  • security.redact_secrets → unconditional ✓

The except Exception: pass at the bottom of the bridge also swallowed any partial bridge failures silently, meaning a raised exception anywhere in the block would skip subsequent keys and leave .env values in place — including HERMES_MAX_ITERATIONS.

Fix

1. gateway/run.py — drop not in os.environ guards for all agent.*, display.*, timezone, and security.* bridge keys. config.yaml is now authoritative for these, matching the semantics already in place for max_turns, terminal.*, and auxiliary.*. Also surface bridge failures to stderr instead of pass so operators see breakage.

2. gateway/run.py — INFO-log the resolved max_iterations at gateway start so operators can verify the bridge did the right thing instead of chasing a phantom budget ceiling:

INFO gateway.run: Agent budget: max_iterations=500 (agent.max_turns from config.yaml, or HERMES_MAX_ITERATIONS from .env, or default 90)

3. hermes_cli/setup.py — stop writing HERMES_MAX_ITERATIONS to .env in the setup wizard. config.yaml is the single source of truth. Also proactively remove any stale .env entry left behind by pre-fix setups on next run.

Tests

  • tests/gateway/test_config_env_bridge_authority.py (new, 166 lines) — regression tests for every bridge key, asserting config.yaml wins even when .env has a conflicting value.
  • tests/hermes_cli/test_setup_agent_settings.py (+55 lines) — covers the setup.py HERMES_MAX_ITERATIONS cleanup path.

Verification

After the fix, running gateway logs:

2026-05-02 01:49:39,618 INFO gateway.run: Agent budget: max_iterations=500 (agent.max_turns from config.yaml, or HERMES_MAX_ITERATIONS from .env, or default 90)

Telegram sessions no longer cap at 60 tool calls per turn.

…ettings

Regression from the silent config→env bridge. The bridge at module import
time is correct for max_turns (unconditional overwrite), but every other
agent.*, display.*, timezone, and security bridge key was guarded by
'if X not in os.environ' — so a stale .env entry from an old 'hermes setup'
run would shadow the user's current config.yaml indefinitely.

Symptom: agent.max_turns: 500 in config.yaml, HERMES_MAX_ITERATIONS=60
in .env from an old setup, and the gateway silently capped at 60
iterations per turn. Gateway logs confirmed api_calls never exceeded 60.

Three changes:

1. gateway/run.py: drop the 'not in os.environ' guards for all agent.*,
   display.*, timezone, and security.* bridge keys. config.yaml is now
   authoritative for these settings — same semantics already in place
   for max_turns, terminal.*, and auxiliary.*. Also surface the bridge
   failure (previously 'except Exception: pass') to stderr so operators
   see bridge errors instead of silently falling back to .env.

2. gateway/run.py: INFO-log the resolved max_iterations at gateway
   start so operators can verify the config→env bridge did the right
   thing instead of chasing a phantom budget ceiling.

3. hermes_cli/setup.py: stop writing HERMES_MAX_ITERATIONS to .env in
   the setup wizard. config.yaml is the single source of truth. Also
   clean up any stale .env entry left behind by pre-fix setups.

Regression tests in tests/gateway/test_config_env_bridge_authority.py
guard each config→env key against the 'stale .env shadows config' bug.
@teknium1
teknium1 merged commit e444d8f into main May 2, 2026
10 of 11 checks passed
@teknium1
teknium1 deleted the fix/gateway-max-turns-bridge-silent-failure branch May 2, 2026 09:14
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/gateway Gateway runner, session dispatch, delivery area/config Config system, migrations, profiles labels May 2, 2026
waym0reom3ga pushed a commit to waym0reom3ga/autolycus-agent that referenced this pull request May 8, 2026
…ettings (NousResearch#18764)

Regression from the silent config→env bridge. The bridge at module import
time is correct for max_turns (unconditional overwrite), but every other
agent.*, display.*, timezone, and security bridge key was guarded by
'if X not in os.environ' — so a stale .env entry from an old 'hermes setup'
run would shadow the user's current config.yaml indefinitely.

Symptom: agent.max_turns: 500 in config.yaml, HERMES_MAX_ITERATIONS=60
in .env from an old setup, and the gateway silently capped at 60
iterations per turn. Gateway logs confirmed api_calls never exceeded 60.

Three changes:

1. gateway/run.py: drop the 'not in os.environ' guards for all agent.*,
   display.*, timezone, and security.* bridge keys. config.yaml is now
   authoritative for these settings — same semantics already in place
   for max_turns, terminal.*, and auxiliary.*. Also surface the bridge
   failure (previously 'except Exception: pass') to stderr so operators
   see bridge errors instead of silently falling back to .env.

2. gateway/run.py: INFO-log the resolved max_iterations at gateway
   start so operators can verify the config→env bridge did the right
   thing instead of chasing a phantom budget ceiling.

3. hermes_cli/setup.py: stop writing HERMES_MAX_ITERATIONS to .env in
   the setup wizard. config.yaml is the single source of truth. Also
   clean up any stale .env entry left behind by pre-fix setups.

Regression tests in tests/gateway/test_config_env_bridge_authority.py
guard each config→env key against the 'stale .env shadows config' bug.
waym0reom3ga pushed a commit to waym0reom3ga/autolycus-agent that referenced this pull request May 8, 2026
…ettings (NousResearch#18764)

Regression from the silent config→env bridge. The bridge at module import
time is correct for max_turns (unconditional overwrite), but every other
agent.*, display.*, timezone, and security bridge key was guarded by
'if X not in os.environ' — so a stale .env entry from an old 'hermes setup'
run would shadow the user's current config.yaml indefinitely.

Symptom: agent.max_turns: 500 in config.yaml, HERMES_MAX_ITERATIONS=60
in .env from an old setup, and the gateway silently capped at 60
iterations per turn. Gateway logs confirmed api_calls never exceeded 60.

Three changes:

1. gateway/run.py: drop the 'not in os.environ' guards for all agent.*,
   display.*, timezone, and security.* bridge keys. config.yaml is now
   authoritative for these settings — same semantics already in place
   for max_turns, terminal.*, and auxiliary.*. Also surface the bridge
   failure (previously 'except Exception: pass') to stderr so operators
   see bridge errors instead of silently falling back to .env.

2. gateway/run.py: INFO-log the resolved max_iterations at gateway
   start so operators can verify the config→env bridge did the right
   thing instead of chasing a phantom budget ceiling.

3. hermes_cli/setup.py: stop writing HERMES_MAX_ITERATIONS to .env in
   the setup wizard. config.yaml is the single source of truth. Also
   clean up any stale .env entry left behind by pre-fix setups.

Regression tests in tests/gateway/test_config_env_bridge_authority.py
guard each config→env key against the 'stale .env shadows config' bug.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants