Skip to content

Propagate write and parse failures instead of swallowing them - #56

Open
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1787186591-error-propagation
Open

Propagate write and parse failures instead of swallowing them#56
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1787186591-error-propagation

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Summary

Four places reported success while losing data. Audit of the error paths in the
export, recipe, and CLI layers; no behavior changes on the success path.

1. Every mesh exporter ignored write failures. std::fopen + fwrite/
fprintf + fclose report a failed write only through the sticky error flag
and fclose's return, and buffered bytes reach the disk at fclose — so a
full disk or I/O error produced a truncated OBJ/MTL/GLB/STL and the CLI printed
in.step -> out.obj. New core/src/out_file.hpp wraps the handle:

detail::OutFile obj(path, "w");
FILE* f = obj.get();
...
obj.finish();  // ferror() || fclose() != 0  ->  remove(path) + throw

The destructor also removes the file when the writer unwinds, so a partial
export is never published as a good one. writeFbx (an ofstream) and
saveRecipe get the same treatment: check after an explicit close(), delete
the partial file, throw.

2. loadRecipe accepted malformed lines. ss >> fid >> list /
ss >> eid >> count were unchecked, so face oops radial=8 silently bound the
override to face 0, and a bad scale/weld value silently reverted to the
default — regenerating a mesh that was not the one the recipe described. Those
lines now throw like the op lines already did, and the existing handler
prefixes path:line:. Out-of-range (non-positive scale, negative tolerance)
still clamps as before; only unparsable input is rejected.

3. The CLI discarded the applyOps report. weft::applyOps(mesh, model, recipe.ops); — the {applied, failed} result was dropped at both call sites
(main path and each LOD tier), so recorded ops that no longer replay were
dropped from the export without a word. Now warns on stderr with the count.
Exit codes are unchanged so the corpus gates keep their current semantics.

4. tessellate() could return an empty mesh as success. BRepMesh signals
per-face failure by leaving the face without a triangulation, and every face
was continued over silently; weft convert <brep> <mesh> then wrote a
geometry-free file and reported success. Skipping some faces is still allowed
(lossy but usable); triangulating none now throws.

New test testRecipeLoadRejectsMalformedLines covers the recipe parse strictness
and that valid overrides still load.

Not changed: the app's catch-to-status-line handlers (correct for a GUI — the
artist sees the message and keeps their session) and the mesher's per-face
isolation in meshers.cpp, which already attributes failures through
buildCause/GenerationReport.

Link to Devin session: https://app.devin.ai/sessions/b8f76bf0032c4f178ec7d250b1e97e3f
Requested by: @jmossymoss

Exporters used stdio/ofstream without checking the sticky error flag or
fclose, so a full disk or I/O error produced a truncated file and reported
success. Recipe loading accepted malformed face/edge/scale/weld lines and
bound them to face 0 or silently reset the value. The CLI discarded the
applyOps report, so recorded ops that failed to replay were dropped
without a word.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@jmossymoss jmossymoss self-assigned this Aug 20, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

jmossymoss and others added 4 commits August 20, 2026 00:45
Without libglfw3-dev the app fetches GLFW 3.4 and its Wayland backend
needs wayland-scanner, which the runner lacks, so configure fails before
anything is compiled.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…n run

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant