fix: OAuth token refresh failure returns undefined instead of throwing

When OAuth refresh fails during model discovery, getApiKey() now returns
undefined instead of throwing. This allows the app to start and fall back
to other providers, so the user can /login to re-authenticate.

fixes #498
This commit is contained in:
Mario Zechner 2026-01-06 20:57:42 +01:00
parent cd797cc407
commit d2f3b42deb
7 changed files with 94 additions and 10 deletions

View file

@ -5,6 +5,7 @@
### Fixed
- Queued steering/follow-up messages no longer wipe unsent editor input ([#503](https://github.com/badlogic/pi-mono/pull/503) by [@tmustier](https://github.com/tmustier))
- OAuth token refresh failure no longer crashes app at startup, allowing user to `/login` to re-authenticate ([#498](https://github.com/badlogic/pi-mono/issues/498))
## [0.37.3] - 2026-01-06

View file

@ -326,7 +326,7 @@ export class AuthStorage {
if (result) {
return result.apiKey;
}
} catch (err) {
} catch {
// Refresh failed - re-read file to check if another instance succeeded
this.reload();
const updatedCred = this.data[provider];
@ -339,13 +339,9 @@ export class AuthStorage {
: updatedCred.access;
}
// Refresh truly failed - DO NOT remove credentials
// User can retry or re-authenticate manually
const errorMessage = err instanceof Error ? err.message : String(err);
throw new Error(
`OAuth token refresh failed for ${provider}: ${errorMessage}. ` +
`Please try again or re-authenticate with /login.`,
);
// Refresh truly failed - return undefined so model discovery skips this provider
// User can /login to re-authenticate (credentials preserved for retry)
return undefined;
}
} else {
// Token not expired, use current access token

View file

@ -171,12 +171,35 @@ function getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager):
return undefined;
}
/**
* Resolve a session argument to a file path.
* If it looks like a path, use as-is. Otherwise try to match as session ID prefix.
*/
function resolveSessionPath(sessionArg: string, cwd: string, sessionDir?: string): string {
// If it looks like a file path, use as-is
if (sessionArg.includes("/") || sessionArg.includes("\\") || sessionArg.endsWith(".jsonl")) {
return sessionArg;
}
// Try to match as session ID (full or partial UUID)
const sessions = SessionManager.list(cwd, sessionDir);
const matches = sessions.filter((s) => s.id.startsWith(sessionArg));
if (matches.length >= 1) {
return matches[0].path; // Already sorted by modified time (most recent first)
}
// No match - return original (will create new session)
return sessionArg;
}
function createSessionManager(parsed: Args, cwd: string): SessionManager | undefined {
if (parsed.noSession) {
return SessionManager.inMemory();
}
if (parsed.session) {
return SessionManager.open(parsed.session, parsed.sessionDir);
const resolvedPath = resolveSessionPath(parsed.session, cwd, parsed.sessionDir);
return SessionManager.open(resolvedPath, parsed.sessionDir);
}
if (parsed.continue) {
return SessionManager.continueRecent(cwd, parsed.sessionDir);

View file

@ -42,7 +42,11 @@ class SessionList implements Component {
}
private filterSessions(query: string): void {
this.filteredSessions = fuzzyFilter(this.allSessions, query, (session) => session.allMessagesText);
this.filteredSessions = fuzzyFilter(
this.allSessions,
query,
(session) => `${session.id} ${session.allMessagesText}`,
);
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredSessions.length - 1));
}