ruvnet/ruflo · error
Cognitum auth service returned an unexpected response
Error message
Cognitum auth service returned an unexpected response: ${e.message} What it means
refreshAccessToken() rethrows an OAuthError from the security package whose code is NOT 'network' — i.e. the Cognitum auth service was reachable but the refresh failed for a protocol/server reason (invalid_grant, server error, malformed token response). The original message from @claude-flow/security is embedded verbatim.
Solutions
- Re-authenticate: run `ruflo auth login --profile <profile>` to obtain fresh tokens (most invalid_grant cases)
- If multiple machines share credentials, stop doing that — rotation with reuse detection will keep invalidating the loser
- Check the embedded e.message: 'invalid_grant' means re-login; HTTP 5xx means wait and retry later
- If it persists after a fresh login, check the auth service status and any proxy between you and auth.cognitum.one
Defensive patterns
Strategy: try-catch
Try / catch
try {
token = await getValidAccessToken(profile);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Cognitum auth service returned an unexpected response')) {
// e.message suffix is the server's OAuthError text: invalid_grant => re-login, 5xx => retry later
if (e.message.includes('invalid_grant')) await promptRelogin(profile);
else await retryLater();
} else throw e;
} Prevention
- Never share one profile's refresh token across machines — rotation with reuse detection will invalidate copies
- Log the embedded server message; it distinguishes revoked tokens from outages
- On invalid_grant, delete local profile state and re-login rather than retrying the stale token
When it happens
Trigger: Calling getValidAccessToken() or refreshAccessToken() when: the persisted refresh token was revoked or expired server-side (invalid_grant); the refresh token was already spent (Cognitum rotates tokens with reuse detection, so a replayed/stale token fails); the server returned 5xx; or the token endpoint responded with an unexpected body/status.
Common situations: Refresh token rotated on another machine sharing the same profile store, so this machine's copy is stale; server-side revocation of old tokens; auth service deployed a breaking change; clock skew or malformed responses via an intercepting proxy.
Related errors
- Cognitum refresh response did not contain an access token
- profile " " has no persisted refresh token and its…
- authorization was denied or failed
- Could not reach the Cognitum auth service. ruflo core…
- login cancelled: no code was entered
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/ceefc5c5d01a0703.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/auth/client.ts:221
* Refreshes an access token. Classifies failure into network-unreachable
* vs. a reachable-but-erroring server so callers can print an honest
* message instead of collapsing both into "offline" (ADR-308 failure
* policy: local ruflo functionality is never affected by auth being
* unavailable, but the diagnostic should say WHY it's unavailable).
*/
export async function refreshAccessToken(refreshTokenValue: string): Promise<OAuthTokenResponse> {
const sec = await loadSecurityOAuth();
try {
return await sec.refreshToken(refreshTokenValue);
} catch (e) {
if (e instanceof sec.OAuthError) {
if (e.code === 'network') {
throw new Error(
'Could not reach the Cognitum auth service. ruflo core functionality is unaffected — ' +
'sign-in is not required for local use.',
);
}
throw new Error(`Cognitum auth service returned an unexpected response: ${e.message}`);
}
throw e;
}
}
/**
* Returns an access token suitable for an authenticated call.
*
* Fast path: a process-memory token with more than one minute remaining.
* Slow path: load the profile's refresh token from the OS keychain, perform
* one refresh, persist a rotated refresh token BEFORE exposing the new access
* token, then update metadata and the process cache. Refresh is deliberately
* demand-driven: offline-safe commands such as plain `auth status` never call
* this function and therefore never create background traffic or retry loops.
*/
export async function getValidAccessToken(profileName = 'default'): Promise<string> {
const profile = getProfile(profileName);
if (!profile) throw new NotLoggedInError(profileName);View on GitHub (pinned to fa13ee4ad6)