hermes - ✅(Solved) Fix bug(status): Nous Tool Gateway shows managed web tools available with revoked/stale Nous auth [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#18072Fetched 2026-05-01 05:54:06
View on GitHub
Comments
2
Participants
2
Timeline
7
Reactions
0
Timeline (top)
labeled ×4commented ×2cross-referenced ×1

hermes status / hermes doctor can report Nous Portal as logged in and managed web tools as available even when the saved Nous auth state is already stale/revoked and managed web calls fail with AUTH_ERROR Unauthorized.

In the current main, status/readiness appears to trust cached auth presence rather than verified refreshability:

  • hermes_cli.auth.get_nous_auth_status() reports logged_in from token presence alone
  • tools.managed_tool_gateway.read_nous_access_token() falls back to returning the cached access token when refresh fails
  • hermes_cli.nous_subscription.get_nous_subscription_features() / hermes status then treat the gateway as ready

Error Message

python - <<'PY' from hermes_cli.auth import get_provider_auth_state, refresh_nous_oauth_from_state state = get_provider_auth_state('nous') or {} try: refresh_nous_oauth_from_state(state, force_refresh=True) print('refresh_ok') except Exception as e: print(type(e).name + ': ' + str(e)) PY

Root Cause

In this state, managed web calls fail with AUTH_ERROR Unauthorized because the cached Nous session is revoked/stale.

Fix Action

Fixed

PR fix notes

PR #18077: fix(tools): fail closed on stale Nous gateway auth

Description (problem / solution / changelog)

Summary

  • Fail closed when an expired Nous Portal token cannot be refreshed for managed tool gateways
  • Preserve legacy cached-token behavior when no expiry metadata is present
  • Add regression coverage for revoked/stale refresh failures disabling gateway readiness

Root cause

read_nous_access_token() attempted to refresh expiring cached Nous tokens, but if refresh raised (for example because the refresh session was revoked), it still returned the stale cached access token. That made managed web/tool gateway readiness report available even though downstream calls would fail with unauthorized errors.

Fix

When refresh is required and fails, return None instead of the stale cached token so is_managed_tool_gateway_ready() fails closed. Cached tokens that have no expiry metadata continue to work for existing legacy auth state.

Regression coverage

Added test_read_nous_access_token_fails_closed_when_refresh_fails, which writes an expired cached Nous token, makes refresh raise Refresh session has been revoked, and verifies both token resolution and gateway readiness return unavailable.

Testing

  • scripts/run_tests.sh tests/tools/test_managed_tool_gateway.py::test_read_nous_access_token_fails_closed_when_refresh_fails -q (RED before fix, GREEN after fix)
  • scripts/run_tests.sh tests/tools/test_managed_tool_gateway.py -q
  • scripts/run_tests.sh tests/tools/test_managed_tool_gateway.py tests/tools/test_web_tools_config.py tests/tools/test_terminal_requirements.py tests/tools/test_terminal_tool_requirements.py -q

Closes #18072

Changed files

  • tests/tools/test_managed_tool_gateway.py (modified, +26/-0)
  • tools/managed_tool_gateway.py (modified, +5/-1)

Code Example

jq '{active_provider, nous: {expires_at: .providers.nous.expires_at, agent_key_expires_at: .providers.nous.agent_key_expires_at, has_refresh_token: (.providers.nous.refresh_token != null and .providers.nous.refresh_token != ""), portal_base_url: .providers.nous.portal_base_url}}' ~/.hermes/auth.json

---

{
  "active_provider": "openai-codex",
  "nous": {
    "expires_at": "2026-04-21T20:06:53.514142+00:00",
    "agent_key_expires_at": "2026-04-22T19:56:25.014Z",
    "has_refresh_token": true,
    "portal_base_url": "https://portal.nousresearch.com"
  }
}

---

python - <<'PY'
from hermes_cli.auth import get_provider_auth_state, refresh_nous_oauth_from_state
state = get_provider_auth_state('nous') or {}
try:
    refresh_nous_oauth_from_state(state, force_refresh=True)
    print('refresh_ok')
except Exception as e:
    print(type(e).__name__ + ': ' + str(e))
PY

---

AuthError: Refresh session has been revoked

---

Auth Providers
  Nous Portal   ✓ logged in
    Portal URL: https://portal.nousresearch.com
    Access exp: 2026-04-21 13:06:53 PDT
    Key exp:    2026-04-22 12:56:25 PDT
    Refresh:    yes

Nous Tool Gateway
  Nous Portal   ✓ managed tools available
  Web tools       ✓ included by subscription, not currently selected

---

python - <<'PY'
from hermes_cli.auth import get_nous_auth_status
from tools.managed_tool_gateway import read_nous_access_token, is_managed_tool_gateway_ready
import json
s = get_nous_auth_status()
print(json.dumps({
  'logged_in': s.get('logged_in'),
  'access_expires_at': s.get('access_expires_at'),
  'agent_key_expires_at': s.get('agent_key_expires_at'),
  'has_refresh_token': s.get('has_refresh_token'),
  'gateway_ready_firecrawl': is_managed_tool_gateway_ready('firecrawl'),
  'token_reader_nonempty': bool(read_nous_access_token()),
}, indent=2))
PY

---

{
  "logged_in": true,
  "access_expires_at": "2026-04-21T20:06:53.514142+00:00",
  "agent_key_expires_at": "2026-04-22T19:56:25.014Z",
  "has_refresh_token": true,
  "gateway_ready_firecrawl": true,
  "token_reader_nonempty": true
}

---

except Exception as exc:
    logger.debug("Nous access token refresh failed: %s", exc)

return cached_token
RAW_BUFFERClick to expand / collapse

Summary

hermes status / hermes doctor can report Nous Portal as logged in and managed web tools as available even when the saved Nous auth state is already stale/revoked and managed web calls fail with AUTH_ERROR Unauthorized.

In the current main, status/readiness appears to trust cached auth presence rather than verified refreshability:

  • hermes_cli.auth.get_nous_auth_status() reports logged_in from token presence alone
  • tools.managed_tool_gateway.read_nous_access_token() falls back to returning the cached access token when refresh fails
  • hermes_cli.nous_subscription.get_nous_subscription_features() / hermes status then treat the gateway as ready

Repro / evidence

Environment: local macOS checkout of main at 93f9db59b.

Local auth state (token values redacted, only metadata shown):

jq '{active_provider, nous: {expires_at: .providers.nous.expires_at, agent_key_expires_at: .providers.nous.agent_key_expires_at, has_refresh_token: (.providers.nous.refresh_token != null and .providers.nous.refresh_token != ""), portal_base_url: .providers.nous.portal_base_url}}' ~/.hermes/auth.json

returns:

{
  "active_provider": "openai-codex",
  "nous": {
    "expires_at": "2026-04-21T20:06:53.514142+00:00",
    "agent_key_expires_at": "2026-04-22T19:56:25.014Z",
    "has_refresh_token": true,
    "portal_base_url": "https://portal.nousresearch.com"
  }
}

Forcing a real refresh against the saved Nous state fails:

python - <<'PY'
from hermes_cli.auth import get_provider_auth_state, refresh_nous_oauth_from_state
state = get_provider_auth_state('nous') or {}
try:
    refresh_nous_oauth_from_state(state, force_refresh=True)
    print('refresh_ok')
except Exception as e:
    print(type(e).__name__ + ': ' + str(e))
PY

Output:

AuthError: Refresh session has been revoked

But status still reports healthy auth + managed web availability:

◆ Auth Providers
  Nous Portal   ✓ logged in
    Portal URL: https://portal.nousresearch.com
    Access exp: 2026-04-21 13:06:53 PDT
    Key exp:    2026-04-22 12:56:25 PDT
    Refresh:    yes

◆ Nous Tool Gateway
  Nous Portal   ✓ managed tools available
  Web tools       ✓ included by subscription, not currently selected

And the underlying helpers also still say the gateway is ready:

python - <<'PY'
from hermes_cli.auth import get_nous_auth_status
from tools.managed_tool_gateway import read_nous_access_token, is_managed_tool_gateway_ready
import json
s = get_nous_auth_status()
print(json.dumps({
  'logged_in': s.get('logged_in'),
  'access_expires_at': s.get('access_expires_at'),
  'agent_key_expires_at': s.get('agent_key_expires_at'),
  'has_refresh_token': s.get('has_refresh_token'),
  'gateway_ready_firecrawl': is_managed_tool_gateway_ready('firecrawl'),
  'token_reader_nonempty': bool(read_nous_access_token()),
}, indent=2))
PY

Output:

{
  "logged_in": true,
  "access_expires_at": "2026-04-21T20:06:53.514142+00:00",
  "agent_key_expires_at": "2026-04-22T19:56:25.014Z",
  "has_refresh_token": true,
  "gateway_ready_firecrawl": true,
  "token_reader_nonempty": true
}

In this state, managed web calls fail with AUTH_ERROR Unauthorized because the cached Nous session is revoked/stale.

Relevant code paths on current main

hermes_cli/auth.py:get_nous_auth_status():

  • pool path returns logged_in=True if an access token string exists
  • fallback path returns logged_in=bool(state.get("access_token"))
  • neither path verifies token freshness or refreshability

tools/managed_tool_gateway.py:read_nous_access_token():

  • tries resolve_nous_access_token() when the token is expiring
  • if refresh fails, catches the exception and returns the stale cached token anyway:
except Exception as exc:
    logger.debug("Nous access token refresh failed: %s", exc)

return cached_token

That makes is_managed_tool_gateway_ready() return true as long as a cached token string exists.

Expected behavior

One of these should happen when the cached Nous auth cannot be refreshed / is revoked:

  1. hermes status / hermes doctor should not show Nous as simply “logged in”; surface a degraded / re-auth-required state.
  2. Managed tool readiness should fail closed when refresh fails, instead of advertising managed web tools as available.
  3. Ideally the status output should include the refresh error (Refresh session has been revoked) or a concise re-auth hint.

Actual behavior

Hermes advertises Nous Tool Gateway availability based on stale cached auth, while managed web calls fail immediately with unauthorized errors.

Notes

This seems distinct from the earlier concurrent-refresh / RT-rotation bugs (#10147 / #15099). Those were about how valid sessions become revoked. This issue is about incorrect auth-status / readiness reporting after the session is already revoked.

extent analysis

TL;DR

The issue can be fixed by modifying hermes_cli/auth.py and tools/managed_tool_gateway.py to verify token freshness and refreshability instead of relying on cached token presence.

Guidance

  • Modify hermes_cli/auth.py:get_nous_auth_status() to check token freshness and refreshability before returning logged_in=True.
  • Update tools/managed_tool_gateway.py:read_nous_access_token() to not return the stale cached token when refresh fails, and instead raise an exception or return an error indicator.
  • Change is_managed_tool_gateway_ready() to return False when the cached token is stale or refresh fails.
  • Consider adding error handling to surface the refresh error or a re-auth hint in the status output.

Example

# hermes_cli/auth.py
def get_nous_auth_status():
    # ...
    if not is_token_fresh(state.get("access_token")) or not can_refresh_token(state):
        return {"logged_in": False}
    # ...

# tools/managed_tool_gateway.py
def read_nous_access_token():
    try:
        # ...
    except Exception as exc:
        # Do not return the stale cached token, raise an exception instead
        raise AuthError("Nous access token refresh failed: {}".format(exc))

Notes

The provided code changes are minimal and only illustrate the suggested modifications. A more comprehensive solution may require additional changes and testing.

Recommendation

Apply the suggested workaround by modifying the relevant code paths to verify token freshness and refreshability, and handle errors accordingly. This should provide a more accurate representation of the auth status and managed tool readiness.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of these should happen when the cached Nous auth cannot be refreshed / is revoked:

  1. hermes status / hermes doctor should not show Nous as simply “logged in”; surface a degraded / re-auth-required state.
  2. Managed tool readiness should fail closed when refresh fails, instead of advertising managed web tools as available.
  3. Ideally the status output should include the refresh error (Refresh session has been revoked) or a concise re-auth hint.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix bug(status): Nous Tool Gateway shows managed web tools available with revoked/stale Nous auth [1 pull requests, 2 comments, 2 participants]