{"record":{"id":"97c85287d2471608","repo":"ruvnet/ruflo","slug":"profile-profile-has-scope-s-without-matching","errorCode":null,"errorMessage":"profile \"${profile}\" has scope(s) without matching local consent: ${scopes.join(', ')} — authenticated capability denied","messagePattern":"profile \"(.+?)\" has scope\\(s\\) without matching local consent: (.+?) — authenticated capability denied","errorType":"exception","errorClass":"ScopeConsentMismatchError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/auth/client.ts","lineNumber":246,"sourceCode":" * Returns an access token suitable for an authenticated call.\n *\n * Fast path: a process-memory token with more than one minute remaining.\n * Slow path: load the profile's refresh token from the OS keychain, perform\n * one refresh, persist a rotated refresh token BEFORE exposing the new access\n * token, then update metadata and the process cache. Refresh is deliberately\n * demand-driven: offline-safe commands such as plain `auth status` never call\n * this function and therefore never create background traffic or retry loops.\n */\nexport async function getValidAccessToken(profileName = 'default'): Promise<string> {\n  const profile = getProfile(profileName);\n  if (!profile) throw new NotLoggedInError(profileName);\n\n  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) {","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/auth/client.ts#L228-L264","documentation":"Thrown by getValidAccessToken (ScopeConsentMismatchError) when the profile's scopes include one or more domains for which no local consent record exists. Consent is a local gate: even with a valid token, scopes lacking consent are denied.","triggerScenarios":"Profile exists but its scopes include a domain with no consent record: consent expired/revoked, a scope added to the profile after login, or a consent store reset.","commonSituations":"Consent store cleared; profile imported between machines without consent; scopes changed server-side after the original login.","solutions":["Re-establish consent for each missing domain (e.g. ruflo auth consent <domain>), or re-login.","Drop the unconsented scope from the profile if it is no longer needed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const missing = profile.scopes.filter(s => {\n  const d = domainForScope(s);\n  return d !== undefined && !hasConsent(d);\n});\nif (missing.length > 0) {\n  throw new Error(`Missing consent for: ${missing.join(', ')}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair every scope grant with a consent write atomically.","Audit consent records whenever a profile is imported or copied.","Re-establish consent when scopes change server-side."],"tags":["oauth","consent","scope","auth"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}