Preserve session.json on transient ListenFatalError#20
Open
DiscoBard wants to merge 1 commit intoMaxGhenis:mainfrom
Open
Preserve session.json on transient ListenFatalError#20DiscoBard wants to merge 1 commit intoMaxGhenis:mainfrom
DiscoBard wants to merge 1 commit intoMaxGhenis:mainfrom
Conversation
OnDisconnect was wired to fire for any libgm ListenFatalError -- including transient network or RPC failures -- and unconditionally removed session.json. On long-running headless deployments this caused a full re-pair to be required after every transient blip, since the session file would not be present at the next start. Issue MaxGhenis#1's intent was for session.json to be removed only when the session itself is invalid (user unpaired from phone, cookies revoked). This forwards the underlying error to the OnDisconnect callback and gates removal on a 401 / SESSION_COOKIE_INVALID classification. Other fatal errors now leave the session intact so the next reconnect can succeed without re-pairing. - internal/client/events.go: OnDisconnect signature now takes an error - internal/app/app.go: classify error via isSessionInvalidated; only delete session.json on confirmed invalidation, otherwise preserve for retry - internal/app/app_test.go: TestIsSessionInvalidated covers nil, 401-status, SESSION_COOKIE_INVALID, wrapped 401, and several transient-error variants Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OnDisconnectwas firing for every libgmListenFatalErrorand unconditionally callingos.Remove(SessionPath). That includes transient fatals (network blip, RPC failure, server-side hiccup), so any such event silently invalidated the saved session and required a full re-pair on the next start.Issue #1's intent — per the closing comment — was for
session.jsonto be removed only when the session itself is invalid (user unpaired from phone, cookies revoked). This change forwards the underlying error to theOnDisconnectcallback and gates removal on a 401 /SESSION_COOKIE_INVALIDclassification. The macOS wrapper'sneeds_pairingflow continues to work for true invalidation; transient disconnects now leavesession.jsonin place so reconnection can proceed without re-pairing.Background
Observed on a long-running headless deployment: the bridge was paired and healthy, then went quiet for a few hours. After the next process restart, startup logged
Google Messages unavailable error="load session ... session.json: no such file or directory". No user-initiated unpair, no obvious 401 in the surviving journal — the session file had been removed by an earlier transientListenFatalError.Implementation
internal/client/events.go—OnDisconnectis nowfunc(err error). TheListenFatalErrorhandler forwardsevt.Error.internal/app/app.go— classifies the disconnect cause via a newisSessionInvalidated(err)helper. Only when the helper returns true does the handler callos.Remove(SessionPath)and surface"session invalidated; pair again". Other errors set"will retry with existing session".internal/app/app_test.go—TestIsSessionInvalidatedcovers nil,401,SESSION_COOKIE_INVALID, afmt.Errorf %w-wrapped 401, and several transient-error strings.The classifier matches on error message substrings (
"401","SESSION_COOKIE_INVALID") since libgm surfaces the cause through the error chain rather than a typed sentinel.Test plan
go build ./...— cleango test ./internal/app/ ./internal/client/— passes (existing + 7 new sub-tests)isSessionInvalidated == trueis unchanged from current behavior)🤖 Generated with Claude Code