Skip to content

NetworkTransport.swift: nonisolated(unsafe) on non-Sendable CheckedContinuation fails on Swift 6.3-dev #211

@doozMen

Description

@doozMen

Related

Relates to #203 — same file, different error site. Together these represent all Swift 6.3 build failures in NetworkTransport.swift.

Description

NetworkTransport.swift fails to build on Swift 6.3 development snapshots due to nonisolated(unsafe) being applied to stored properties of type CheckedContinuation, which is not Sendable.

Swift 6.3 tightens enforcement: nonisolated(unsafe) now requires the annotated type to conform to Sendable. CheckedContinuation<T, E> is intentionally non-Sendable (SE-0302) to prevent double-resume at the type level.

To Reproduce

# Using any Swift 6.3 development snapshot
TOOLCHAINS=org.swift.dev.snapshot.2026-03-16-a swift build

Compiler Errors

NetworkTransport.swift:56:30: error: 'nonisolated(unsafe)' global or static variables must be of type 'Sendable'
    nonisolated(unsafe) var transportContinuation: CheckedContinuation<NWConnection, any Error>?
                             ^

NetworkTransport.swift:57:30: error: 'nonisolated(unsafe)' global or static variables must be of type 'Sendable'
    nonisolated(unsafe) var stoppedContinuation: CheckedContinuation<Void, any Error>?
                             ^

Note: The diagnostic says "global or static variables" but these are actor instance properties — this appears to be a misleading compiler diagnostic.

Environment

  • Builds successfully on: Swift 6.0.3 release
  • Fails on: Swift 6.3-dev (snapshot 2026-03-16-a)
  • Package swift-tools-version: 6.0

Root Cause

Lines 56-57 of NetworkTransport.swift:

nonisolated(unsafe) var transportContinuation: CheckedContinuation<NWConnection, any Error>?
nonisolated(unsafe) var stoppedContinuation: CheckedContinuation<Void, any Error>?

These properties are accessed from NWConnection's stateUpdateHandler callback, which runs off-actor (on the connection's dispatch queue). The nonisolated(unsafe) annotation was used to suppress actor isolation errors, but Swift 6.3 now requires the type itself to be Sendable.

Suggested Fix

Use a lock-protected wrapper that is Sendable and works on all currently supported platforms (macOS 13+/iOS 16+), without requiring import Synchronization (which would bump minimums to macOS 15+/iOS 18+):

/// Thread-safe wrapper for cross-isolation access. Sendable because
/// all access is serialized through NSLock.
private final class LockedValue<Value>: @unchecked Sendable {
    private var _value: Value
    private let lock = NSLock()
    
    init(_ value: Value) { self._value = value }
    
    func withLock<T>(_ body: (inout Value) -> T) -> T {
        lock.lock()
        defer { lock.unlock() }
        return body(&_value)
    }
}

Then replace:

nonisolated(unsafe) var transportContinuation: CheckedContinuation<NWConnection, any Error>?
nonisolated(unsafe) var stoppedContinuation: CheckedContinuation<Void, any Error>?

With:

private let transportContinuation = LockedValue<CheckedContinuation<NWConnection, any Error>?>(nil)
private let stoppedContinuation = LockedValue<CheckedContinuation<Void, any Error>?>(nil)

This preserves the existing platform minimums, compiles on both Swift 6.0.3 and 6.3-dev, and has the same API shape as Mutex.withLock for future migration when platform minimums are bumped.

Also worth noting (relates to #203)

The @MainActor usage in sendData() and receiveData() (lines 531, 762) is a separate but related issue. @MainActor is being used purely as a serialization mechanism for continuation guard booleans — but this is an MCP protocol library with no UI. The @MainActor hop adds unnecessary main-thread contention and is semantically incorrect for a headless protocol SDK. The sendHeartbeat() method in the same file (line ~460) does NOT use this guard pattern, resuming the continuation directly — suggesting the guard was added reactively rather than by design.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions