You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Swift backend: toWire does not unwrap RawRepresentable (named string-union) fields, breaking wire round-trip #331
The Swift backend's provider wire-mapping path (toWire) does not unwrap RawRepresentable (named string-union / alias) fields — it stores the generated struct instead of its backing rawValue string. A wire round-trip (toWire → fromWire → load) then fails because the loader expects a String.
Discovered in microsoft/prompty after upgrading to @typra/emitter2.1.8. The new toWire/fromWire wire-conversion tests added in 2.1.8 (fix for #328) surfaced this latent Swift-only bug.
Affected type
ModelOptions.reasoningEffort — the only field on that type with a named string-union alias type, rendered by the Swift backend as a RawRepresentable struct:
// save() — CORRECT
iflet value =self.reasoningEffort {result["reasoningEffort"]= value.rawValue
}
But the provider wire-mapping toWire() path does not:
// toWire() — BUG: stores the ReasoningEffort struct, not its String
iflet wireKey = wireNameReasoningEffort,let value =self.reasoningEffort {result[wireKey]= value }
fromWire() maps the wire key back to the canonical field and calls load(), whose loader does TypraRuntime.string(value, field: "reasoningEffort"). Because the value is a ReasoningEffort struct rather than a String, it throws:
Expected string for field reasoningEffort.
Reproduction
Any schema with a ModelOptions-like model that has a named string-union alias field mapped to a provider wire name (e.g. @@knownAs(ModelOptions.reasoningEffort, "openai", "reasoning_effort")). The generated ModelOptionsTests.testWireConversion fails on Swift only; Python, C#, Rust, Go, Java, and TypeScript all pass (their toWire paths emit the backing string correctly).
Expected
The Swift toWire path should unwrap RawRepresentable/named-union fields via .rawValue, matching the save() path:
iflet wireKey = wireNameReasoningEffort,let value =self.reasoningEffort {result[wireKey]= value.rawValue }
Scope
Swift backend only. Applies to any RawRepresentable-typed (named string-union / alias) field that participates in provider wire mapping — not just reasoningEffort.
We patch the generated assignment to append .rawValue in a deterministic post-emit normalization step so the regenerate-no-diff gate stays green. Would prefer to drop the workaround once the emitter unwraps these fields in the Swift toWire path.
Confirmed on main: src/languages/swift/emitter.ts:937/941 calls swiftSaveExpression(mapping.category, null, ...) in the toWire path, so enumName is always null and the .rawValue unwrap never happens. save() passes the enum name, which is why it works.
Activity
schema: reference sethjuarez/typra#331 in Swift reasoningEffort worka…
sethjuarez commentedon Oct 13, 2026
Confirmed on main:
src/languages/swift/emitter.ts:937/941callsswiftSaveExpression(mapping.category, null, ...)in the toWire path, soenumNameis always null and the.rawValueunwrap never happens.save()passes the enum name, which is why it works.fix(emitter): serialize string-union fields correctly in Swift and Ja…