I've found out that basic-cli's Cmd.status! doesn't actually have a convenient way to get the status if it is non-zero, so you get e.g. (CmdStatusErr (Other "Non-zero exit code: 2")). While the type is Cmd => Result I32 ..., the I32 can only be 0 in practice.
In accordance with Hyrum's Law, people are now relying on this behavior and are not actually checking the returned status. So I think we should correct the behavior of status and also (temporarily) rename the function to increase the likelihood that people become aware of this change in behavior.
We currently have:
status! : Cmd => Result I32 [CmdStatusErr InternalIOErr.IOErr]
I propose we turn this into:
## To replace the old use of the incorrect `status`
execute! : Cmd => Result {} [CmdErr InternalIOErr.IOErr]
## For those we actually want clean access to the exit code
exec_status! : Cmd => Result I32 [CmdErr InternalIOErr.IOErr]
What do you think?
I'm going ahead with this proposal given the lack of objections.
Sorry, I think I have an objection here
Go ahead :)
Isn't the behaviour your describing above just a bug in our implementation... like it should return Ok(2) instead of the error.
Oh yes, I agree
But I did not just want to fix the bug because "people are now relying on this behavior and are not actually checking the returned status"
oof... hmm :upside_down:
I would still lean towards fixing it.
It's a breaking change either way
Yes, but it is important that it is not a silent change in behavior breaking change
Would giving a nice big warning label be enough here?
If someone upgrades to basic-cli 0.20 and they don't check the release notes and everything looks like it's working, they will never see the warning label
On the other hand, now everone has to change thier scripts to rename that function
So it's garunteed pain for everyone, or possibly a little pain for a few that dont check the upgrade notes
And given where the platform is at in terms of maturity, being more experimental, I think it would be ok to make this kind of trade off
A significant amount of people will not check the upgrade notes, especially if they're going from 0.19 to 0.23 or something like that.
Avoiding silent failures has been important in Roc and I feel strongly about it
I don't feel strongly about this...
How about just fixing it and renaming to execute. No need yo change the API or add another function.
Or even just exec!
We already have exec!
My proposed execute! : Cmd => Result {} [CmdErr InternalIOErr.IOErr] was to fill the missing analog to exec!
exec! is for reference:
exec! : Str, List Str => Result {} [CmdStatusErr InternalIOErr.IOErr]
Cmd => Result {} [CmdErr InternalIOErr.IOErr] seems like something a lot of people would want when they don't care about the specific exit code but just success or failure.
seems like something a lot of people would want
This is already confirmed by how people are currently using status! without using the exit code:
https://github.com/ostcar/kingfisher/blob/6c4e83c28c0483fe126f8e10ac37d00d795344ef/build.roc#L24
https://github.com/isaacvando/basic-webserver/blob/ead72acb6e22c7cc74c2634d4bf83618ea1a924c/examples/todos.roc#L147
https://github.com/isaacvando/rtl/blob/9c3ea0a2b8e9eabce1800e2d6a4be2ed932615b2/rtl.roc#L114
https://github.com/megakilo/roc2048/blob/e92736a0db35e0e9ddf31e1e41349768d2ddf3c0/main.roc#L33
https://github.com/adomurad/r2e-platform/blob/6ff0c102e1322f71379edd46d4ec6d7e887634dc/build.roc#L108
In accordance with Hyrum's Law, people are now relying on this behavior and are not actually checking the returned status.
I think we should ignore this. Roc is too early to care about this and we would build up a ton of tech debt if we lock in things like this early. All version bumps are breaking changes today.
## To replace the old use of the incorrect `status`
execute! : Cmd => Result {} [CmdErr InternalIOErr.IOErr]
## For those we actually want clean access to the exit code
exec_status! : Cmd => Result I32 [CmdErr InternalIOErr.IOErr]
This API looks fine. Though having one be exec and the other be execute feels inconsistent.
One thing I do think we should change though. We probably should change the error type to explicitly understand the exit code. Instead of Other "Non-zero exit code: 2", it should be NonZeroExitCode 2
But yeah, general change seems like a good idea
Though having one be
execand the other beexecutefeels inconsistent.
exec_cmd is another possibility but Cmd.exec_cmd is a bit weird too
By the way, I thought status was a bad function name way before this. If a beginning programmer was asked "what happens if you call status on a command?", I think they would not guess the true behavior.
Yeah, and Cmd.execute_returning_status is way too long
People may rarely need this function though, so it does not need to be short. I only want to use it because roc returns a specific exit code if there is a warning but no errors. I don't know how common it is to need to branch on a specific exit code in case of failure.
Not sure how common it is. Have seen it with git bisection as well. I'm sure there are other cases.
Last updated: Jun 16 2026 at 16:19 UTC