{"record":{"id":"3d2bb0ec899d26ec","repo":"ruvnet/ruflo","slug":"cognitum-refresh-response-did-not-contain-an-acces","errorCode":null,"errorMessage":"Cognitum refresh response did not contain an access token","messagePattern":"Cognitum refresh response did not contain an access token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/auth/client.ts","lineNumber":259,"sourceCode":"  const scopesWithoutConsent = profile.scopes.filter((scope) => {\n    const domain = domainForScope(scope);\n    return domain !== undefined && !hasConsent(domain);\n  });\n  if (scopesWithoutConsent.length > 0) {\n    throw new ScopeConsentMismatchError(profileName, scopesWithoutConsent);\n  }\n\n  const cached = getSessionToken(profileName, ACCESS_TOKEN_REFRESH_WINDOW_MS);\n  if (cached) return cached;\n  if (!profile.keychainRef) throw new SessionOnlyExpiredError(profileName);\n\n  const sec = await loadSecurityOAuth();\n  const keychain = await sec.createKeychainAdapter();\n  const refreshTokenValue = await keychain.getSecret(KEYCHAIN_SERVICE, profile.keychainRef);\n  if (!refreshTokenValue) throw new SessionOnlyExpiredError(profileName);\n\n  const refreshed = await refreshAccessToken(refreshTokenValue);\n  if (!refreshed.access_token) throw new Error('Cognitum refresh response did not contain an access token');\n\n  // Cognitum rotates refresh tokens with reuse detection. Commit the rotated\n  // credential first; if this write fails, do not publish/cache the access\n  // token and do not retry the already-spent old refresh token here.\n  if (refreshed.refresh_token) {\n    await keychain.setSecret(KEYCHAIN_SERVICE, profile.keychainRef, refreshed.refresh_token);\n  }\n\n  const expiresAtMs = Date.now() + Math.max(0, refreshed.expires_in ?? 0) * 1000;\n  setSessionToken(profileName, refreshed.access_token, expiresAtMs);\n  setProfile(profileName, {\n    ...profile,\n    accountId: refreshed.account_email ?? profile.accountId,\n    accessTokenExpiresAt: new Date(expiresAtMs).toISOString(),\n    linkedAt: new Date().toISOString(),\n  });\n  return refreshed.access_token;\n}","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/auth/client.ts#L241-L277","documentation":"Thrown by getValidAccessToken() after the refresh-token HTTP round-trip to Cognitum's token endpoint succeeded (resolved, not rejected) but the parsed response body has no access_token field. This is a service-contract violation, distinct from network-unreachable failures (those are classified separately inside refreshAccessToken). The guard prevents caching or publishing a useless empty token.","triggerScenarios":"Any authenticated ruflo command runs after the in-memory session token expired (less than 60s left) AND a refresh token exists in the OS keychain AND refreshAccessToken() resolves but refreshed.access_token is falsy (undefined, null, or empty string).","commonSituations":"Cognitum auth service shipped a breaking response-shape change; a corporate proxy rewrote or stripped the JSON body; the security package's refreshToken() parsed a non-OAuth response (HTML error page served with HTTP 200); clock skew or a misconfigured endpoint returns an error payload inside a 200.","solutions":["Re-run `ruflo auth login --profile <name>` to obtain a fresh token pair — persistent service-contract drift cannot be fixed by retrying the spent refresh token","Check @claude-flow/security release notes for a token-response parser update and upgrade if a newer version exists","Inspect the raw HTTP response from auth.cognitum.one (enable security package debug logging) to confirm whether the body is empty or shaped differently","If behind a proxy, verify it passes JSON response bodies through unmodified"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const token = await getValidAccessToken(profile);\n} catch (e) {\n  if (e instanceof Error && /did not contain an access token/.test(e.message)) {\n    // The refresh token was spent but the service returned a malformed body.\n    // Do NOT retry getValidAccessToken — the rotated refresh token is already\n    // committed. Prompt the user to re-authenticate from scratch.\n    console.error('Auth service returned a malformed token response. Run: ruflo auth login --profile ' + profile);\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Keep @claude-flow/security updated so the token-response parser matches the current Cognitum contract","Monitor auth service changelog for breaking changes to the token endpoint response shape","In automated/CI environments, prefer --token-stdin with a freshly minted token over relying on refresh"],"tags":["auth","oauth","cognitum","token-refresh"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}