{"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":null,"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/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/auth/client.ts#L241-L277","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isMissingAccessTokenResponse(e: unknown): boolean {\n  return e instanceof Error && e.message === 'Cognitum refresh response did not contain an access token';\n}","tryCatchPattern":"try {\n  token = await getValidAccessToken(profileName);\n} catch (e) {\n  if (isMissingAccessTokenResponse(e)) {\n    // server contract violation: one retry, then re-login; report if it persists\n    await sleep(5000);\n    token = await getValidAccessToken(profileName).catch(() => { throw reloginRequired(); });\n  } else throw e;\n}","preventionTips":["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"],"tags":["auth","oauth","refresh-token","server-contract","protocol-error"],"backgroundTag":"oauth-refresh-response-invalid","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}