ruvnet/ruflo · error
Cognitum refresh response did not contain an access token
Error message
Cognitum refresh response did not contain an access token
What it means
Defense-in-depth guard after refreshAccessToken() succeeds: the OAuthTokenResponse from the Cognitum refresh endpoint had no access_token. refreshToken() normally converts protocol failures into OAuthError, so reaching this line with a token-less response means the server (or something impersonating it) answered in a shape that passed earlier parsing but is unusable — a server contract violation, not a client input problem.
Solutions
- Retry once after a short pause — transient mid-deployment responses usually resolve
- Re-login to get fresh tokens: ruflo auth login --profile <profile>
- If reproducible, inspect what the endpoint actually returns (env HTTPS_PROXY debugging) and report it — this indicates a server-side contract break
- Check for intercepting proxies/TLS middleboxes rewriting responses
Defensive patterns
Strategy: try-catch
Type guard
function isMissingAccessTokenResponse(e: unknown): boolean {
return e instanceof Error && e.message === 'Cognitum refresh response did not contain an access token';
} Try / catch
try {
token = await getValidAccessToken(profileName);
} catch (e) {
if (isMissingAccessTokenResponse(e)) {
// server contract violation: one retry, then re-login; report if it persists
await sleep(5000);
token = await getValidAccessToken(profileName).catch(() => { throw reloginRequired(); });
} else throw e;
} Prevention
- Monitor for this error — it should be near-zero and signals an auth-server regression
- Check for intercepting proxies that rewrite error bodies into 200 responses
- Re-login rather than hammering the endpoint when it repeats
When it happens
Trigger: The refresh endpoint returns HTTP 200 with a JSON body that omits access_token (e.g. only a refresh_token, or an error object smuggled in a 200); a misbehaving proxy returns a success-shaped but empty token payload; the auth service has a bug/deployment regression.
Common situations: Auth service mid-deployment returning inconsistent responses; API gateways that rewrite error bodies into 200s; novel server-side changes to the token response schema not yet reflected in the client expectation.
Related errors
- Cognitum auth service returned an unexpected response
- 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/3d2bb0ec899d26ec.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/auth/client.ts:259
const scopesWithoutConsent = profile.scopes.filter((scope) => {
const domain = domainForScope(scope);
return domain !== undefined && !hasConsent(domain);
});
if (scopesWithoutConsent.length > 0) {
throw new ScopeConsentMismatchError(profileName, scopesWithoutConsent);
}
const cached = getSessionToken(profileName, ACCESS_TOKEN_REFRESH_WINDOW_MS);
if (cached) return cached;
if (!profile.keychainRef) throw new SessionOnlyExpiredError(profileName);
const sec = await loadSecurityOAuth();
const keychain = await sec.createKeychainAdapter();
const refreshTokenValue = await keychain.getSecret(KEYCHAIN_SERVICE, profile.keychainRef);
if (!refreshTokenValue) throw new SessionOnlyExpiredError(profileName);
const refreshed = await refreshAccessToken(refreshTokenValue);
if (!refreshed.access_token) throw new Error('Cognitum refresh response did not contain an access token');
// Cognitum rotates refresh tokens with reuse detection. Commit the rotated
// credential first; if this write fails, do not publish/cache the access
// token and do not retry the already-spent old refresh token here.
if (refreshed.refresh_token) {
await keychain.setSecret(KEYCHAIN_SERVICE, profile.keychainRef, refreshed.refresh_token);
}
const expiresAtMs = Date.now() + Math.max(0, refreshed.expires_in ?? 0) * 1000;
setSessionToken(profileName, refreshed.access_token, expiresAtMs);
setProfile(profileName, {
...profile,
accountId: refreshed.account_email ?? profile.accountId,
accessTokenExpiresAt: new Date(expiresAtMs).toISOString(),
linkedAt: new Date().toISOString(),
});
return refreshed.access_token;
}View on GitHub (pinned to fa13ee4ad6)