Skip to content

Result Errors

tyc::invalid_question_op

? is rejected in two situations (the v0.9.0 help text covers both explicitly):

1. Outside a Result-returning function

def f() -> int:
let n: int = parse_port("8080")? # ❌ — function returns int, not Result
return n

Fix: change the signature, or match explicitly.

def f() -> Result[int, str]:
let n: int = parse_port("8080")?
return Ok(n)

2. Inside a comprehension

Comprehensions lower to nested for-loops in Python, so the surrounding function frame ? would short-circuit out of is not the comprehension’s frame:

def collect(xs: list[str]) -> Result[list[int], str]:
return Ok([parse(x)? for x in xs]) # ❌ — `?` inside a comprehension

Fix: pre-extract with an explicit loop, or chain with .and_then / .map:

def collect(xs: list[str]) -> Result[list[int], str]:
mut out: list[int] = []
for x in xs:
out.append(parse(x)?)
return Ok(out)

tyc::result_error_mismatch

? short-circuits an Err[E1] to a function returning Result[T, E2] where E1 is not assignable to E2:

def parse_str() -> Result[int, str]: ...
def f() -> Result[int, AppError]:
let n: int = parse_str()? # ❌ — str not assignable to AppError
return Ok(n)

Fix: convert at the boundary with .map_err:

def f() -> Result[int, AppError]:
let n: int = parse_str().map_err(lambda msg: AppError(message=msg))?
return Ok(n)

Or unfold the bridge with match:

def f() -> Result[int, AppError]:
match parse_str():
case Ok(n):
return Ok(n)
case Err(msg):
return Err(AppError(message=msg))

Since v0.8.0 the same check fires inside with-chain ?-bindings — every binding’s implicit propagation path is validated against the enclosing function’s declared error type. Since v0.9.0 the explicit else err: return Err(err) form goes through the same check too; previously the validation was gated on the synthetic ?-op temp shape, so an else block could silently return an Err whose payload type didn’t match the function’s declared return.

Closed Result method surface (v0.13.0)

The Result / Ok / Err method surface is now closed. Calling an unknown method on a Result value — a typo like .unwarp() — fires tyc::attribute_not_found at check time. Before v0.13.0 an unknown method passed the checker and crashed at runtime with AttributeError.

def f() -> Result[int, str]:
let r: Result[int, str] = parse_port("8080")
return Ok(r.unwarp()) # ❌ tyc::attribute_not_found — typo for `.unwrap`

The known method set is:

  • Combinators: .map / .map_err / .and_then / .or_else.
  • Unwrap / query family: .unwrap() / .expect(msg) / .unwrap_or(d) / .unwrap_or_else(f) / .ok() / .err() / .is_ok() / .is_err().

Fix: correct the method name, or match on the Result if you need a shape these methods don’t cover.

tyc::result_unused (reserved)

Future-reserved diagnostic for a Result-returning call whose value is discarded:

def save() -> Result[None, str]: ...
save() # would warn: Result not consumed

Not yet emitted by the compiler. Today, an ignored Result is silently dropped; the diagnostic name is reserved so the eventual rule has a stable code from day one. Use ? or match to consume Result values today.

Where next