mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-22 16:25:58 +00:00
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:
parent
d358280ad7
commit
77beb6a085
2 changed files with 87 additions and 2 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue