ruvnet/ruflo · error · NotLoggedInError
not logged in for profile
Error message
not logged in for profile "${profile}" — run: ruflo auth login --profile ${profile} What it means
getValidAccessToken(profileName) throws NotLoggedInError when getProfile(profileName) returns nothing — there is no stored auth profile for that name. The message includes the exact remediation command. Profiles are created by `ruflo auth login`; asking for a profile that never logged in (or a typo'd name) hits this immediately, before any network traffic.
Solutions
- Run the command from the message: ruflo auth login --profile <profile>
- Check which profiles exist first: ruflo auth status
- If the name was a typo, call getValidAccessToken with the exact profile name you logged in as
- On ephemeral CI machines, perform a login step (e.g. --token-stdin) before any authenticated command
Defensive patterns
Strategy: try-catch
Validate before calling
// Cheap pre-check: run `ruflo auth status` (offline-safe) and assert the profile is listed const status = await runRuflo(['auth', 'status', '--json']); if (!status.profiles.includes(profileName)) await runRuflo(['auth', 'login', '--profile', profileName]);
Type guard
import { NotLoggedInError } from '@claude-flow/cli/dist/auth/client.js';
function isNotLoggedIn(e: unknown): e is NotLoggedInError {
return e instanceof Error && e.name === 'NotLoggedInError';
} Try / catch
try {
token = await getValidAccessToken(profileName);
} catch (e) {
if (isNotLoggedIn(e)) {
console.error(e.message); // already contains the exact login command
process.exit(3);
}
throw e;
} Prevention
- Run `ruflo auth status` at session start on CI/new machines
- Treat profile names as config: validate them once at startup
- Catch by error name (NotLoggedInError), not by parsing the human message
When it happens
Trigger: Calling getValidAccessToken('work') when only the 'default' profile exists; calling with the default when no login ever happened; a typo or case-mismatched profile name ('Default' vs 'default'); the profile state file was deleted or never created on a fresh machine/CI container.
Common situations: New machine or CI runner with no prior `ruflo auth login`; scripts hard-coding a profile name that doesn't match what the user created; state directory removed during cleanup (~/.claude-flow or equivalent wiped); switching between accounts and forgetting to log in under the new profile.
Related errors
- login cancelled: no code was entered
- --token-stdin: JSON is missing required field "access_token"
- --token-stdin: no input received on stdin
- authorization was denied or failed
- Cognitum auth service returned an unexpected response
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/42e7f81f81e8228f.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/auth/client.ts:239
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);
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);
View on GitHub (pinned to fa13ee4ad6)