I'm getting this error:
── ✗ type mismatch ────────────────────────────────────────── src/main.roc:9:5
This ? may return early with a type that doesn't match the function body.
Stdout.line!("Hello,World!")?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If this Try is an Err, then the ? after it immediately returns an Err whose
payload has this type:
[StdoutErr(Str)]
Returning an Err with that type only works if the function itself returns a Try
with a compatible error type, but this function's return type is:
Try({}, [Exit(I32), StdoutErr(Str)])
Hint: The error types from all ? operators and the function body must be
compatible, since any of them could be the actual return value.
and I cant figure out what it means. The types look compatible. Is it a bug?
I also cant figure out how to replicate it in a minimal setup
Which platform are you using?
What is the type annotation for the platforms Stdout.line!?
Which platform are you using?
My own
Was trying to make a raylib one but I was getting errors, so I started over and was trying to just remake the zig template platform first to have a reference while learning how to make a platform.
What is the type annotation for the platforms
Stdout.line!?
Host.roc:
Host := [].{
stderr_line! : Str => Try({}, [StderrErr(Str)])
stdin_line! : {} => Try(Str, [StdinErr(Str)])
stdout_line! : Str => Try({}, [StdoutErr(Str)])
}
Stdout.roc:
import Host
Stdout := [].{
line! : Str => Try({}, [StdoutErr(Str)])
line! = Host.stdout_line!
}
main.roc:
app [main!] { roc: "nightly-2026-10-04-130536d", pf: platform "platform/main.roc" }
import pf.Stdout
main! : List(Str) => Try({}, [Exit(I32), StdoutErr(Str)])
main! = |_args| {
Stdout.line!("Hello,World!")?
Ok({})
}
@Jared Ramirez is this a bug?
I feel like this should work, but I might be tired and missing something obvious here.
Does it work if you use main! : List(Str) => Try({}, _) i.e. add a _ type hole instead?
yup, this is a known issue! currently polarity does not apply to host functions return types (ie returned tag unions are not implicitly opened), because the host ABI contract requires a closed union exactly
to fix, i believe we will need to implement an implicit remapping when you use a host function like this, along the lines of an implicit match to instantiate it as open
Does it work if you use
main! : List(Str) => Try({}, _)i.e. add a_type hole instead?
── ✗ type mismatch ──────────────────────────────────────────── src/main.roc:7:9
The platform requires main! to have a specific type.
main! = |_args| {
^^^^^^^
Here it has the type:
List(Str) => Try({}, [StdoutErr(Str)])
But the platform requires:
List(Str) => Try({}, [Exit(I32)])
The requirement is declared here:
requires {} { main! : List(Str) => Try({}, [Exit(I32), ..]) }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
yup, this is a known issue! currently polarity does not apply to host functions return types
ok I understand what is happening then. Just need to make a wrapper then.
Thanks for the help!
johan alley has marked this topic as resolved.
I think it's fine if platform authors remap explicitly
@Jared Ramirez I'm not sure if we should remap automatically :sweat_smile:
yeah fair, if we don't tho, we should try to improve the error message at least!
@johan alley would you mind making a ticket for that with your example above?
I'm not sure if we should remap automatically
@Richard Feldman why though? I expect most of the platform functions I make wont need a wrapper (other than to avoid this issue)
short answer is that we've found a number of scenarios where re-mapping is something we could do...but there consistently end up being pitfalls during implementation, and it's still not clear to me that it's a good idea to hide such a significant cost as a (potentially large) match
Last updated: Oct 15 2026 at 17:56 UTC