diff --git a/legacy/builder/constants/constants.go b/legacy/builder/constants/constants.go
index 5773f4d770d..7e35760adb3 100644
--- a/legacy/builder/constants/constants.go
+++ b/legacy/builder/constants/constants.go
@@ -86,7 +86,9 @@ const MSG_ERROR_ARCHIVING_CORE_CACHE = "Error archiving built core (caching) in
 const MSG_CORE_CACHE_UNAVAILABLE = "Unable to cache built core, please tell {0} maintainers to follow https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-build-the-corea-archive-file"
 const MSG_BOARD_UNKNOWN = "Board {0} (platform {1}, package {2}) is unknown"
 const MSG_BOOTLOADER_FILE_MISSING = "Bootloader file specified but missing: {0}"
-const MSG_BUILD_OPTIONS_CHANGED = "Build options changed, rebuilding all"
+const MSG_REBUILD_ALL = ", rebuilding all"
+const MSG_BUILD_OPTIONS_CHANGED = "Build options changed"
+const MSG_BUILD_OPTIONS_INVALID = "{0} invalid"
 const MSG_CANT_FIND_SKETCH_IN_PATH = "Unable to find {0} in {1}"
 const MSG_FQBN_INVALID = "{0} is not a valid fully qualified board name. Required format is targetPackageName:targetPlatformName:targetBoardName."
 const MSG_SKIP_PRECOMPILED_LIBRARY = "Skipping dependencies detection for precompiled library {0}"
diff --git a/legacy/builder/wipeout_build_path_if_build_options_changed.go b/legacy/builder/wipeout_build_path_if_build_options_changed.go
index 57be59838d6..fbb621626ea 100644
--- a/legacy/builder/wipeout_build_path_if_build_options_changed.go
+++ b/legacy/builder/wipeout_build_path_if_build_options_changed.go
@@ -40,9 +40,15 @@ func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(ctx *types.Context) error {
 	previousBuildOptionsJson := ctx.BuildOptionsJsonPrevious
 
 	var opts *properties.Map
+	if err := json.Unmarshal([]byte(buildOptionsJson), &opts); err != nil || opts == nil {
+	    panic(constants.BUILD_OPTIONS_FILE + " is invalid")
+	}
+
 	var prevOpts *properties.Map
-	json.Unmarshal([]byte(buildOptionsJson), &opts)
-	json.Unmarshal([]byte(previousBuildOptionsJson), &prevOpts)
+	if err := json.Unmarshal([]byte(previousBuildOptionsJson), &prevOpts); err != nil || prevOpts == nil {
+		ctx.GetLogger().Println(constants.LOG_LEVEL_DEBUG, constants.MSG_BUILD_OPTIONS_INVALID + constants.MSG_REBUILD_ALL, constants.BUILD_OPTIONS_FILE)
+		return doCleanup(ctx.BuildPath)
+	}
 
 	// If SketchLocation path is different but filename is the same, consider it equal
 	if filepath.Base(opts.Get("sketchLocation")) == filepath.Base(prevOpts.Get("sketchLocation")) {
@@ -73,7 +79,7 @@ func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(ctx *types.Context) error {
 func doCleanup(buildPath *paths.Path) error {
 	// FIXME: this should go outside legacy and behind a `logrus` call so users can
 	// control when this should be printed.
-	// logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_BUILD_OPTIONS_CHANGED)
+	// logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_BUILD_OPTIONS_CHANGED + constants.MSG_REBUILD_ALL)
 
 	if files, err := buildPath.ReadDir(); err != nil {
 		return errors.WithMessage(err, "cleaning build path")
diff --git a/test/test_compile.py b/test/test_compile.py
index c1374aaf607..ffff1931092 100644
--- a/test/test_compile.py
+++ b/test/test_compile.py
@@ -1021,3 +1021,29 @@ def test_compile_with_conflicting_libraries_include(run_command, data_dir, copy_
     assert 'Multiple libraries were found for "OneWire.h"' in lines
     assert f"Used: {one_wire_lib_path}" in lines
     assert f"Not used: {one_wire_ng_lib_path}" in lines
+
+
+def test_compile_with_invalid_build_options_json(run_command, data_dir):
+    assert run_command("update")
+
+    assert run_command("core install arduino:avr@1.8.3")
+
+    sketch_name = "CompileInvalidBuildOptionsJson"
+    sketch_path = Path(data_dir, sketch_name)
+    fqbn = "arduino:avr:uno"
+
+    # Create a test sketch
+    assert run_command(f"sketch new {sketch_path}")
+
+    # Get the build directory
+    sketch_path_md5 = hashlib.md5(bytes(sketch_path)).hexdigest().upper()
+    build_dir = Path(tempfile.gettempdir(), f"arduino-sketch-{sketch_path_md5}")
+
+    assert run_command(f"compile -b {fqbn} {sketch_path} --verbose")
+
+    # Breaks the build.options.json file
+    build_options_json = build_dir / "build.options.json"
+    with open(build_options_json, "w") as f:
+        f.write("invalid json")
+
+    assert run_command(f"compile -b {fqbn} {sketch_path} --verbose")