Repository navigation
Roll back a rejected XML load as a whole, and keep Tree's destructor from throwing - #1289
Merged
facontidavide merged 5 commits intoNov 12, 2026
Merged
Conversation
BehaviorTree#1266 dropped a document that failed to load, but the rollback stopped at the document boundary. If main.xml includes sub.xml and then fails, sub.xml has already registered its trees and they stay behind, and sub.xml becomes the first document, so instantiateTree() reads main_tree_to_execute from it. Take a snapshot before the call and restore it on failure. That also puts back a tree this call redefined, whose element lives in the document about to be erased. Fixes BehaviorTree#1282
Tree::~Tree() calls haltTree(), and halting may throw: a TryCatch with catch_on_halt="true" ticks its catch child, and a tick is allowed to throw. An exception leaving a destructor calls std::terminate, so destroying such a tree aborted the process. Mark the destructor noexcept and swallow the exception. There is no caller left to report to. Fixes BehaviorTree#1281
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
|
let me ask this. Is this a REAL problem YOU have in PRACTICE, or something reported by AI? I don't have any problem with AI, but I feel like this is solving a problem nobody has |
- loadLastDocImpl is now only the top-level rollback point: <include> calls loadDocImpl directly, and on failure the call drops every document it opened (erase(doc_it, end())), so a valid included document no longer stays the "first document" whose main_tree_to_execute instantiateTree() reads. The snapshot also covers subtree_models and current_path, as listed in BehaviorTree#1282, and is restored by move. - ~Tree(): drop the redundant noexcept (destructors are implicitly noexcept) and report a throwing halt on stderr, as FinallyNode does, instead of swallowing it silently (also fixes bugprone-empty-catch). - Tests: share TempSubdir/writeFile through test_helper.hpp (no getpid, which broke MSVC); move the destructor test next to the other catch_on_halt tests using lambda nodes and assert the catch child ran; add a first-document test; make the redefinition test go through a valid include of a rejected file (the old one passed on master); drop HaltStillRuns and RollbackKeepsEarlierTrees, which asserted nothing new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
facontidavide
force-pushed
the
fix-1282-1281-clean
branch
from
November 12, 2026 21:15
9928a59 to
60e22c9
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
I used DeepSeek as a tool, but the analysis is my own |
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Two fixes for #1282 and #1281.
#1282 - #1266 dropped a document that failed to load, but the rollback
stopped at the document boundary. If
main.xmlincludessub.xmland thenfails,
sub.xmlhas already registered its trees and they stay behind;sub.xmlalso becomes the first document, soinstantiateTree()readsmain_tree_to_executefrom it. Take a snapshot oftree_roots/tree_sources/suffix_countbefore the call and restore it onfailure. That also puts back a tree the call redefined, whose element lives in
the document about to be erased.
#1281 -
Tree::~Tree()callshaltTree(), and halting may throw: aTryCatchwithcatch_on_halt="true"ticks its catch child, and a tick isallowed to throw. An exception leaving a destructor calls
std::terminate, sodestroying such a tree aborted the process. Mark the destructor
noexceptandswallow the exception.
Both come with tests that fail on the current master:
XMLParserTest.RejectedIncludeKeepsNoTreeandRejectedInlineIncludesKeepNoTreesee the included tree survive a rejected load.TreeDestructorTest.HaltThatThrowsDoesNotTerminateaborts withterminate called after throwing an instance of 'BT::NodeExecutionError'.Full suite: 684 passed, 2 skipped. The XML side is also clean under
-fsanitize=address.The
noexceptonTree::~Tree()changes a public header. If you would ratherkeep the signature alone, dropping the keyword and keeping the try/catch in the
body is enough - say so and I will change it.
Maintainer changes (rebased on master after #1290)
<include>now callsloadDocImpldirectly, and only the top-level load call snapshots and restores. On failure it drops every document the call opened (erase(doc_it, end())). Before, a valid included document stayed behind as the "first document" whosemain_tree_to_executea later load picked up. The snapshot also coverssubtree_modelsandcurrent_path, as listed in XMLParser: a rejected file's valid includes stay registered #1282.~Tree(). Thenoexceptis gone (destructors are implicitlynoexcept). A throwing halt is now reported on stderr, asFinallyNodedoes, instead of being swallowed silently.tests/test_helper.hpp, which removes thegetpidcall that failed on MSVC.catch_on_halttests and checks that the catch child ran.RejectedIncludeIsNotTheFirstDocumenttest.Behavior change: after a rejected load, trees from its valid includes are no longer registered. A load either succeeds completely or leaves the parser as it was.
Follow-up, not in this PR: a node that throws while halting still stops
haltTree()before it reaches the remaining nodes. The PR preventsstd::terminate, but sibling nodes may missonHalted().