Repository navigation
fix(connection): settle legacy reservations and end failed grants - #89
Merged
Merged
Conversation
A session whose latest mobile_connections row was written by the 000039
flow ('pending' or 'accepted') answered 409 to every QR code create or
refresh: 000041 left those rows in place, and change() treats a status it
does not know as still active. Migration 000044 moves them to their final
claim-flow equivalents, 'lapsed' and 'released'.
A failed additional-administrator grant only recorded provision_error and
stayed confirmed/provisioning. That blocked new QR codes the same way, and
the recovery queue re-ran the grant every 10 seconds, restarting the VTA
each time. connection.MarkFailed now moves it to 'failed', which is final,
and the API returns the error for failed requests.
Signed-off-by: Geoff Turk <geoff@ic3.dev>
vthwang
approved these changes
Oct 21, 2026
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.
Background
On staging, linking the existing VTA
gtto Keyring failed: every "Generate new QR code" click returned409 connection already claimed or VTA not readyfromPOST /api/v1/setup/gt/mobile-connections/3910eca4-…/refresh.Querying the staging database showed the session's latest
mobile_connectionsrow was3910eca4-f152-4201-9bad-0dd218b9448d, statuspending, nooperation, created 4 October under the original mobile-connection flow (000039). Migration000041added the claim-reservation statuses but left existing rows untouched.connection.changetreats a latest row whose status it doesn't recognize as still active (!finalStatus→ErrConflict), and neitherpendingnoracceptedis infinalStatus, so that session could never create or refresh a QR code again.gtwas not alone: 21 staging sessions had a legacypending(2) oraccepted(19) row as their latest reservation.gtwas unblocked by hand by setting its one row tolapsed; this PR fixes the rest, and production if it carries the same rows.Reading the same code path turned up a second way to reach the same permanent 409. When adding an additional administrator (
grant_acl) fails,failAdditionalMobileonly recordedprovision_errorand left the requestconfirmed/provisioning. Its message told the user to "Generate a new QR code and try again", which the API then refused. WithORCHESTRATOR_RESUME=truethe recovery queue also re-ran the grant every 10 seconds, restarting the VTA each time it got far enough to scale it down. This one did not cause thegtfailure.Changes
migrations/000044_settle_legacy_mobile_connections: movespending→lapsedandaccepted→released, fillingreleased_at(required by the000041constraint) fromconnected_at,provisioned_at,accepted_atorcreated_at. The down migration is a no-op: the settled rows can't be told apart from claim-flow rows, and000041's down mapslapsed/releasedback anyway.internal/connection/store.go: newMarkFailedmoves agrant_aclrequest inconfirmed/provisioningtofailedwith its error.failedis final, so a new QR code can be generated and the recovery queue no longer picks the request up.internal/handler/mobile_additional.go:failAdditionalMobilecallsMarkFailed. This includes the case where the grant ran but the VTA did not restart: that used to retry forever, and now ends asfailed(a new attempt finds the DID already present).internal/handler/mobile_connection.go: theerrorfield is also returned forfailedrequests.internal/apidocs/openapi.yaml:failedadded to both reservation status enums;errordescribed.internal/connection/store_test.go:TestFailedGrantFreesSession(409 during a grant, fresh reservation after it fails) andTestLegacyReservationsSettled(legacy rows answer 409 before the migration, settle to the right status andreleased_at, then allow a new reservation).Follow-up
The portal (
vtafarm) doesn't know thefailedstatus yet. It degrades safely, showing "Generate a QR code when you are ready." and the "Generate new QR code" button, but it doesn't show the error, so the user isn't told the last attempt failed. A small change inSessionPnmCard.tsxandsrc/lib/api.ts.Testing
go test ./...against a disposable Postgres 18 container (VTAFARM_TEST_DATABASE_URL): all packages pass, including the two new tests.