Diagnosed live: a call placed from Android reached /e2e/turn, an envelope was sent, and no push was ever attempted.
The chain. PushKit issues the VoIP token before APNs issues the alert token. The iOS upload closure sends PushRegistrar.shared.currentToken ?? "" — an empty alert token — beside a perfectly good voip_token. The route treated an empty token as "forget this device", deleted the row, and returned 200. The device then had no push row at all, so pushToDevices found nothing and never called Apple. No APNs error, because there was no APNs call.
The evidence lined up exactly: four /e2e/push/token calls today all returning 200, updated_at on both surviving rows still yesterday (the model has @Timestamp(on: .update), so a write would have moved it), call setup reaching /e2e/turn, and zero APNs errors in 24h.
Two changes. Deleting is gated on there being no VoIP token either, and neither token is overwritten with an empty string — so whichever half arrives second cannot wipe the half that arrived first.
This is the half that repairs existing installs without another build. The iOS side should also stop sending "" at all, which is a separate change and needs a release.
Tests: 697 swift-testing, 2 new XCTest, 0 failures.
Diagnosed live: a call placed from Android reached `/e2e/turn`, an envelope was sent, and **no push was ever attempted**.
**The chain.** PushKit issues the VoIP token before APNs issues the alert token. The iOS upload closure sends `PushRegistrar.shared.currentToken ?? ""` — an empty alert token — beside a perfectly good `voip_token`. The route treated an empty `token` as "forget this device", deleted the row, and returned **200**. The device then had no push row at all, so `pushToDevices` found nothing and never called Apple. No APNs error, because there was no APNs call.
**The evidence lined up exactly:** four `/e2e/push/token` calls today all returning 200, `updated_at` on both surviving rows still yesterday (the model has `@Timestamp(on: .update)`, so a write would have moved it), call setup reaching `/e2e/turn`, and zero APNs errors in 24h.
**Two changes.** Deleting is gated on there being no VoIP token either, and neither token is overwritten with an empty string — so whichever half arrives second cannot wipe the half that arrived first.
**This is the half that repairs existing installs without another build.** The iOS side should also stop sending `""` at all, which is a separate change and needs a release.
Tests: 697 swift-testing, 2 new XCTest, 0 failures.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Diagnosed live: a call placed from Android reached
/e2e/turn, an envelope was sent, and no push was ever attempted.The chain. PushKit issues the VoIP token before APNs issues the alert token. The iOS upload closure sends
PushRegistrar.shared.currentToken ?? ""— an empty alert token — beside a perfectly goodvoip_token. The route treated an emptytokenas "forget this device", deleted the row, and returned 200. The device then had no push row at all, sopushToDevicesfound nothing and never called Apple. No APNs error, because there was no APNs call.The evidence lined up exactly: four
/e2e/push/tokencalls today all returning 200,updated_aton both surviving rows still yesterday (the model has@Timestamp(on: .update), so a write would have moved it), call setup reaching/e2e/turn, and zero APNs errors in 24h.Two changes. Deleting is gated on there being no VoIP token either, and neither token is overwritten with an empty string — so whichever half arrives second cannot wipe the half that arrived first.
This is the half that repairs existing installs without another build. The iOS side should also stop sending
""at all, which is a separate change and needs a release.Tests: 697 swift-testing, 2 new XCTest, 0 failures.
mentioned in commit
bbd20df74d