Skip to content

Remove on-device network-type detection + release 0.1.0 - #5

Merged
JakubMrozek merged 2 commits into
mainfrom
fix/network-monitor-double-resume
Jul 2, 2026
Merged

JakubMrozek merged 2 commits into
mainfrom
fix/network-monitor-double-resume

Conversation

@JakubMrozek

@JakubMrozek JakubMrozek commented Jul 2, 2026 •

Copy link
Copy Markdown
Member

Summary

Fixes a hard crash (EXC_BREAKPOINT / SIGTRAP) reported from production (Polybuzz / PolySpeak — NetworkInfoProvider.observeNetworkType() in the crash log), by removing the crashing code path rather than guarding it.

NetworkInfoProvider resolved network.type by bridging NWPathMonitor.pathUpdateHandler into a CheckedContinuation:

monitor.pathUpdateHandler = { path in
    ...
    monitor.cancel()
    continuation.resume(returning: type)   // ← fires twice → trap
}

pathUpdateHandler is not one-shot — it re-fires on every path change, and monitor.cancel() does not retract callbacks already dispatched to the monitor's (concurrent) queue. On a flapping/constrained network a second callback resumes the same continuation twice, which Swift's checked continuation turns into a fatal _assertionFailure. The reporter's crash frame is nw_path_is_constrained — a second path update carrying a constrained-status change.

This ran inside DeviceCollector.collectAsync() → on every /preload (every chat message), so at scale it produced a steady, high-volume crash cluster concentrated on mobile users. It shipped in every release since 1.0.5 (Sept 2025) — not a v4 regression; v4 just relocated the code here.

Why remove instead of guard

We confirmed against the ad server (apps/ad-server) that network.type is not used for ad selection, targeting, filtering, or pricing — validation has every network field optional, and type only maps to OpenRTB device.connectiontype forwarded to DSPs. So there is nothing to preserve. A continuation that no longer exists cannot be double-resumed → this eliminates the crash class instead of mitigating it.

What changes

  • type detection removed — the entire NWPathMonitor path (observeNetworkType / currentNetworkType) is deleted. type is now always nil.
  • carrier and detail dropped — carrier was already always nil on iOS 16+; detail (cellular RAT) was only meaningful gated on type, which is gone. The mapRadioAccess table + CoreTelephony/Network imports go with them.
  • userAgent unchanged — the one field the server actually consumes (tracked in the preload_requests analytics table).
  • type/carrier remain in NetworkInfo as always-nil fields so the shape stays stable and a value can be reintroduced later without a breaking change.

Tests

Rewrote NetworkInfoProviderTests for the simplified contract: type/carrier always nil, bridge dict omits nil fields, userAgent surfaced verbatim. Full suite green (166 tests) on the simulator; the network suite dropped from ~127s to ~31s (no more NWPathMonitor waits).

Release

This PR includes the 0.1.0 release bump (CHANGELOG + podspec s.version). Minor bump (0.0.5 → 0.1.0) because removing NetworkInfo.detail and making type optional is a breaking API change for direct consumers — flagged with a ### Breaking note. After merge: tag 0.1.0 and pod trunk push per RELEASING.md.

Follow-up (sequenced after this release)

sdk-swift must bump KontextKit (currently pinned exact: "0.0.4") and adapt its consumer: NetworkInfo.type is now String? and NetworkInfo.detail is gone, so DeviceCollector won't compile until updated. To actually send network.type = null on the wire, sdk-swift's own NetworkDTO.type should become optional and forward nil (today it would coerce to .other). Then release a new KontextSwiftSDK — that's the version Polybuzz updates to get the fix. Same for the RN/Flutter iOS halves.

🤖 Generated with Claude Code

`NetworkInfoProvider` resolved `network.type` by bridging
`NWPathMonitor.pathUpdateHandler` into a `CheckedContinuation`. That
handler is not one-shot — it re-fires on every path change, and
`monitor.cancel()` does not retract callbacks already dispatched to the
monitor's queue. On a flapping/constrained network a second callback
resumed the continuation twice, trapping the process with EXC_BREAKPOINT
(SIGTRAP). This ran on every `/preload` (every chat message), so at scale
it produced a steady, high-volume production crash concentrated on mobile
users. The bug shipped in every release since 1.0.5 (Sept 2025).

Rather than guard the continuation, remove the code path entirely: the ad
server does not use `network.type` for ad selection (it only forwards it
to DSPs as OpenRTB `connectiontype`), so there is nothing to keep. `type`
is now always nil. `carrier` (already always nil on iOS 16+) and `detail`
(cellular radio access tech, only meaningful alongside `type`) are dropped
too. `userAgent` — the one field the server actually consumes (tracked in
preload analytics) — is unchanged.

A continuation that no longer exists cannot be double-resumed, so this
eliminates the crash class rather than mitigating it. `type`/`carrier`
stay in `NetworkInfo` as always-nil fields so the shape is stable and a
value can be reintroduced later without a breaking change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JakubMrozek
JakubMrozek force-pushed the fix/network-monitor-double-resume branch from 9eef295 to 942118c Compare July 2, 2026 13:00
@JakubMrozek JakubMrozek changed the title Fix NWPathMonitor double-resume crash in NetworkInfoProvider Remove on-device network-type detection (fixes NWPathMonitor double-resume crash) Jul 2, 2026
Removal of on-device network-type detection (NWPathMonitor double-resume
crash). CHANGELOG entry + podspec version bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JakubMrozek JakubMrozek changed the title Remove on-device network-type detection (fixes NWPathMonitor double-resume crash) Remove on-device network-type detection + release 0.1.0 Jul 2, 2026
@JakubMrozek
JakubMrozek merged commit 16ccf0f into main Jul 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant