fix(slack): set non-retryable fatal error on missing Slack credentials

Missing SLACK_BOT_TOKEN / SLACK_APP_TOKEN is a permanent configuration
error, not a transient outage. Without a fatal-error marker the gateway
queued Slack for background reconnection and looped forever (#66696).
Set _set_fatal_error(..., retryable=False) so the reconnect watcher
drops it from the retry queue, and point the log/error text at
`hermes gateway setup` / the profile's ~/.hermes/.env.

Salvaged from PR #66720 by @x7peeps. Fixes #66696.
This commit is contained in:
x7peeps 2026-07-22 03:53:43 -07:00 committed by Teknium
parent d358280ad7
commit 77beb6a085
2 changed files with 87 additions and 2 deletions

View file

@ -1131,10 +1131,34 @@ class SlackAdapter(BasePlatformAdapter):
app_token = os.getenv("SLACK_APP_TOKEN")
if not raw_token:
logger.error("[Slack] SLACK_BOT_TOKEN not set")
logger.error(
"[Slack] SLACK_BOT_TOKEN not set — this is a permanent config "
"error; set SLACK_BOT_TOKEN via `hermes gateway setup` "
"or in the active profile's ~/.hermes/.env file, then restart "
"the gateway.",
)
self._set_fatal_error(
"missing_slack_bot_token",
"SLACK_BOT_TOKEN not configured. Use `hermes gateway setup` "
"or add it to your active profile's ~/.hermes/.env file, "
"then restart the gateway.",
retryable=False,
)
return False
if not app_token:
logger.error("[Slack] SLACK_APP_TOKEN not set")
logger.error(
"[Slack] SLACK_APP_TOKEN not set — this is a permanent config "
"error; set SLACK_APP_TOKEN via `hermes gateway setup` "
"or in the active profile's ~/.hermes/.env file, then restart "
"the gateway.",
)
self._set_fatal_error(
"missing_slack_app_token",
"SLACK_APP_TOKEN not configured. Use `hermes gateway setup` "
"or add it to your active profile's ~/.hermes/.env file, "
"then restart the gateway.",
retryable=False,
)
return False
proxy_url = _resolve_slack_proxy_url()

View file

@ -5192,3 +5192,64 @@ class TestThreadContextAppMessages:
)
assert "hello" in content # the real message survives; empty bot msg dropped
# ---------------------------------------------------------------------------
# Missing-credential handling — fatal-error contract
# ---------------------------------------------------------------------------
class TestMissingCredentials:
"""Missing SLACK_BOT_TOKEN or SLACK_APP_TOKEN must set a non-retryable fatal error."""
@pytest.mark.asyncio
async def test_missing_bot_token_sets_fatal_error(self):
"""When SLACK_BOT_TOKEN is absent from both config and env, connect()
must set fatal_error with code 'missing_slack_bot_token' and retryable=False."""
config = PlatformConfig(enabled=True, token=None) # no bot token
adapter = SlackAdapter(config)
fatal_errors = []
def capture_fatal(code, message, *, retryable):
fatal_errors.append({"code": code, "message": message, "retryable": retryable})
with (
patch.object(adapter, "_set_fatal_error", side_effect=capture_fatal),
patch.dict(os.environ, {}, clear=True),
):
result = await adapter.connect()
assert result is False
assert len(fatal_errors) == 1
assert fatal_errors[0]["code"] == "missing_slack_bot_token"
assert fatal_errors[0]["retryable"] is False
assert "SLACK_BOT_TOKEN" in fatal_errors[0]["message"]
assert "hermes gateway setup" in fatal_errors[0]["message"].lower() or ".env" in fatal_errors[0]["message"]
@pytest.mark.asyncio
async def test_missing_app_token_sets_fatal_error(self):
"""When SLACK_APP_TOKEN is absent but SLACK_BOT_TOKEN is present,
connect() must set fatal_error with code 'missing_slack_app_token'
and retryable=False."""
config = PlatformConfig(enabled=True, token="xoxb-fake")
adapter = SlackAdapter(config)
fatal_errors = []
def capture_fatal(code, message, *, retryable):
fatal_errors.append({"code": code, "message": message, "retryable": retryable})
with (
patch.object(adapter, "_set_fatal_error", side_effect=capture_fatal),
patch.dict(os.environ, {"SLACK_BOT_TOKEN": "xoxb-fake"}, clear=True),
):
result = await adapter.connect()
assert result is False
assert len(fatal_errors) == 1
assert fatal_errors[0]["code"] == "missing_slack_app_token"
assert fatal_errors[0]["retryable"] is False
assert "SLACK_APP_TOKEN" in fatal_errors[0]["message"]
assert "hermes gateway setup" in fatal_errors[0]["message"].lower() or ".env" in fatal_errors[0]["message"]