Take the block's default exit when a condition's arithmetic fails - #348
Closed
joaothallis wants to merge 1 commit into
Closed
joaothallis wants to merge 1 commit into
joaothallis wants to merge 1 commit into
Conversation
joaothallis
force-pushed
the
arithmetic-error-in-condition-takes-default-exit
branch
from
September 17, 2026 00:08
ed86d96 to
faa28fd
Compare
A flow author's Case-block condition can divide by zero (or otherwise
fault) at runtime even when every operand is a valid number. `Kernel.//2`
then raises `ArithmeticError`, which is neither an `Expression.Error` nor a
`RuntimeError`, so it escaped `evaluate_expression_block/2`'s rescue and
crashed the calling process instead of just failing the condition.
Treat a runtime arithmetic fault like any other unusable condition value:
`Exit.evaluate/2` and `Case.evaluate_outgoing/5` already fall through to the
block's default exit for an `{:error, _, _}` result, so the run continues.
Also reorder `Exit.evaluate/2`'s case clauses: the `{:error, reason,
bad_parts}` clause was already unreachable, shadowed by the preceding
`other when not is_boolean(other)` guard. The typed error return now makes the
dead clause visible to the compiler, so move the specific clause first.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
joaothallis
force-pushed
the
arithmetic-error-in-condition-takes-default-exit
branch
from
September 17, 2026 00:46
faa28fd to
f71cd17
Compare
Contributor
Author
|
Superseded by turnhub/expression#372. The cleaner fix is in the expression library, which already reraises a RuntimeError raised during evaluation as Expression.Error; ArithmeticError was simply a missing case in that clause. With that, evaluate_expression_block/2's existing Expression.Error handling already routes a division-by-zero condition to the default exit, so no flow_runner change is needed. |
joaothallis
deleted the
arithmetic-error-in-condition-takes-default-exit
branch
September 17, 2026 01:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A condition expression on a Case block can perform arithmetic that faults at runtime even when every operand is a valid number, division by zero being the canonical case.
Kernel.//2raisesArithmeticError, which is neither anExpression.Errornor aRuntimeError, so it slipped pastevaluate_expression_block/2's rescue and crashed the process evaluating the flow instead of simply failing the condition.What
evaluate_expression_block/2now rescuesArithmeticErrorand returns{:error, :arithmetic, message}. Both condition paths,Case.evaluate_outgoing/5andExit.evaluate/2, already treat an{:error, _, _}result as an unusable condition and fall through to the block's default exit, so the run continues. This is exactly how a non-numeric operand (anExpression.Error) already behaves.Exit.evaluate/2'scaseclauses. Its{:error, reason, bad_parts}clause was already unreachable, shadowed by the precedingother when not is_boolean(other)guard; the typed error return now makes the dead clause visible to the compiler.Tests
evaluate_expression_block/2returns{:error, :arithmetic, _}instead of raising.Scope
Targeted at
ArithmeticError(division by zero is the observed high-volume case). Non-numeric operands are already handled viaExpression.Error; both now fail the same way, by taking the block's default exit.🤖 Generated with Claude Code