Skip to content

Crash using PropertyListEncoder in an actor #85703

@mattneub

Description

@mattneub

Description

If an Encodable is wrapped up as a value in a dictionary, and you encode the dictionary with PropertyListEncoder in a background actor, the app crashes with the curious claim: "Could not cast value of type Whatever to 'Swift.Encodable', even though the type Whatever is clearly marked as Encodable.

Reproduction

import Foundation

struct Thing: Codable {
    let name: String
}

actor ActorWhoEncodes {
    var dict = [String: Thing]()

    func encode(thing: Thing) throws -> Data? {
        dict["testing"] = thing
        return try PropertyListEncoder().encode(dict)
    }
}

And then, elsewhere, something of this sort (run on the main actor):

        Task {
            try await ActorWhoEncodes().encode(thing: Thing(name: "matt"))
            // Crash! Could not cast value of type 'PropertyListEncoderTest.Thing'
            // to 'Swift.Encodable'
        }

Expected behavior

Either we should not crash or we should not have compiled in the first place.

Environment

swift-driver version: 1.127.14.1 Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1)
Target: arm64-apple-macosx26.0

Additional information

Ultimately I found a workaround! Namely, conform nonisolated to Codable:

struct Thing: nonisolated Codable {

So, whether there is a bug here and what the bug is, I leave to you. I can think of three possible responses:

  • No bug. This is all expected and is what you get for not saying nonisolated Codable in the first place, if you were thinking of using PropertyListEncoder in an actor.
  • The bug is the failure of the compiler to call out the problem. It's interesting that if you tell the PropertyListEncoder to encode(thing), you get a helpful compiler error: "Main actor-isolated conformance of 'Thing' to 'Encodable' cannot be used in actor-isolated context". Thus, one might argue that the real trouble here is that when what we encode is a Thing wrapped in a Dictionary, there is no compile-time error. However, it may be that the compiler just doesn't have the cojones to think as deep as it would need to in order to notice the issue.
  • It's a bug (and perhaps is just a sign of issues with using PropertyListEncoder under Swift concurrency; it wouldn't be the first time someone has pointed that out).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.triage neededThis issue needs more specific labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions