Skip to content

Fillet radius past the feasible limit silently builds wrong geometry; the loud error only fires in a ~0.01-wide pocket #1297

Description

@sneg55

Context: API Makeathon participant. Found with Breakpoint, a tool built for the makeathon that sweeps one numeric KCL parameter, bisects every pass/fail boundary, scans past it for failure pockets, and cross-checks "successful" builds against analytic feasibility oracles.

On a 10 mm cube with a single vertical edge fillet, driving filletRadius past its feasible limit does not produce an error for almost the entire infeasible range. The engine builds a shape, reports smoothly varying metrics, and exports STEP with no warning, all the way to r = 30 on a 10 mm part, where 97% of the cube's volume is gone. The engine error that does exist fires only in a narrow pocket just past the limit, roughly 0.01 wide in a swept range of 30.

Related: #3056 in KittyCAD/modeling-app asks for the error message to state the maximum feasible radius. This report is about the stronger problem behind it: past the limit the engine usually does not error at all.

Fixture

@settings(defaultLengthUnit = mm)

cubeSize = 10
filletRadius = 2

cube = startSketchOn(XY)
  |> startProfile(at = [0, 0])
  |> line(end = [cubeSize, 0], tag = $e1)
  |> line(end = [0, cubeSize], tag = $e2)
  |> line(end = [-cubeSize, 0], tag = $e3)
  |> close(tag = $e4)
  |> extrude(length = cubeSize)
  |> fillet(radius = filletRadius, tags = [getNextAdjacentEdge(e2)])

Run with zoo kcl analyze <file> --format json --volume-output-unit mm3, substituting filletRadius.

Measured map of the boundary

The true tangent-fillet limit on this part is 10.0 (the adjacent faces are 10 mm). Measured behavior around and past it:

filletRadius exit code result
2 0 991.365 mm3, correct (analytic quarter-round removal matches)
9.99 0 785.179 mm3, correct
10.01 1 loud engine error (text below)
10.3 0 silent success, 772.039 mm3
30 0 silent success, 28.882 mm3 of a 1000 mm3 cube

STEP export of the r = 12 part succeeds with no warning.

Bisection brackets the erroring pocket at roughly r in [10.005, 10.01], with silent successes on both sides. So pass/fail is not monotonic in the radius: a coarse parameter scan steps straight over the only loud signal and sees nothing but successes.

The error inside the pocket:

KCL Engine error

  × engine: The Zoo engine cannot handle this 3D subtraction yet.  Please
  │ report this as an issue
  │ Edge cut failed
    ╭─[14:6]
 13 │   |> extrude(length = cubeSize)
 14 │   |> fillet(radius = filletRadius, tags = [getNextAdjacentEdge(e2)])

Why this matters more than the cryptic error

A radius-30 fillet on a 10 mm cube cannot be a tangent fillet: no analytic reading of the source can produce a valid part. The engine nonetheless returns success, plausible-looking metrics, and a clean STEP export. Anyone driving the API programmatically (parameter sweeps, optimization loops, AI-generated KCL where nobody hand-checked the numbers) receives wrong geometry with a success status. The failure the user can see (the #3056 error) is confined to a pocket 0.3% of the range; the failure they cannot see covers the rest.

Suggested behavior: an infeasible fillet radius should be an error everywhere past the limit, ideally the "maximum radius for this fillet is X" message #3056 asks for.

Environment

  • Zoo CLI v0.2.184 (latest release) against https://api.zoo.dev/, production engine
  • First measured 2026-07-22, re-verified value-for-value 2026-07-24 (no engine drift between the two dates)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions