From 5652085eb4d884f300a7db7f2dbc522ecacd2e8e Mon Sep 17 00:00:00 2001 From: Peter Dvorak Date: Wed, 1 Jul 2026 09:46:06 +0200 Subject: [PATCH] Fix checker crashes silently passing validation as valid The two catch blocks in checkBehaviorReport() assumed every caught error was a custom {path, error} object (as thrown by Behavior.createStructureInfo / checkStatemachine). A real JS exception (e.g. a TypeError from a bug in the checker) has no .error property, so: - checking.js:189 pushed `error.error` == undefined onto report.fatal_errors. checkBehavior() then returns fatal_errors[0] == undefined, which callers read as "no error" -> a behavior that actually failed the check could be saved or started as if valid. - checking.js:199 called `error.path.replace(...)` unconditionally. On a real exception .path is undefined, so this threw a second TypeError inside the catch handler, masking the original error. Both handlers now resolve a non-empty message (error.error -> error.message -> String(error)), so a checker crash always produces a real fatal error instead of undefined. The container-navigation logic is guarded by `if (error.path != undefined)` so it only runs for the custom {path, error} objects that carry a path. --- flexbe_webui/app/_helper/checking.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/flexbe_webui/app/_helper/checking.js b/flexbe_webui/app/_helper/checking.js index 71e31a5..d39f620 100644 --- a/flexbe_webui/app/_helper/checking.js +++ b/flexbe_webui/app/_helper/checking.js @@ -186,7 +186,8 @@ const Checking = new (function() { } catch (error) { console.log(`\x1b[91m Failed behavior check for '${Behavior.getBehaviorName()}\x1b[0m'\n ${error}`); console.log(error.stack); - report.fatal_errors.push(error.error); + // error may be a custom {path, error} object or a real JS exception; always record a non-empty message + report.fatal_errors.push(error.error != undefined ? error.error : (error.message != undefined ? error.message : `${error}`)); return report; } @@ -196,16 +197,21 @@ const Checking = new (function() { } catch (error) { console.log(`\x1b[91m Failed behavior createStructureInfo for '${Behavior.getBehaviorName()}\x1b[0m'\n ${error}`); console.log(error.stack); - let container_path = error.path.replace("/"+error.path.split("/").pop(), ""); - let container = Behavior.getStatemachine().getStateByPath(container_path); - if (container instanceof BehaviorState) { - error.error += "
Note: Since this error is inside a contained behavior, please open this behavior directly and fix it there."; - error.error += "
Affected behavior: " + container.getBehaviorName(); - container = container.getBehaviorStatemachine(); - } - UI.Statemachine.setDisplayedSM(container); + // error may be a custom {path, error} object or a real JS exception; resolve a non-empty message and only + // navigate to the offending container when a path is actually present (a real exception has none). + let error_message = error.error != undefined ? error.error : (error.message != undefined ? error.message : `${error}`); + if (error.path != undefined) { + let container_path = error.path.replace("/"+error.path.split("/").pop(), ""); + let container = Behavior.getStatemachine().getStateByPath(container_path); + if (container instanceof BehaviorState) { + error_message += "
Note: Since this error is inside a contained behavior, please open this behavior directly and fix it there."; + error_message += "
Affected behavior: " + container.getBehaviorName(); + container = container.getBehaviorStatemachine(); + } + UI.Statemachine.setDisplayedSM(container); + } UI.Menu.toStatemachineClicked(); - report.fatal_errors.push(error.error); + report.fatal_errors.push(error_message); return report; }