diff --git a/.licenses/go/github.com/arduino/go-paths-helper.dep.yml b/.licenses/go/github.com/arduino/go-paths-helper.dep.yml
index 01899363c84..86f71e419a7 100644
--- a/.licenses/go/github.com/arduino/go-paths-helper.dep.yml
+++ b/.licenses/go/github.com/arduino/go-paths-helper.dep.yml
@@ -1,6 +1,6 @@
 ---
 name: github.com/arduino/go-paths-helper
-version: v1.12.1
+version: v1.13.0
 type: go
 summary: 
 homepage: https://pkg.go.dev/github.com/arduino/go-paths-helper
diff --git a/.licenses/go/golang.org/x/sys/execabs.dep.yml b/.licenses/go/golang.org/x/sys/execabs.dep.yml
index 8f6b0e7d019..d2eeeb205c3 100644
--- a/.licenses/go/golang.org/x/sys/execabs.dep.yml
+++ b/.licenses/go/golang.org/x/sys/execabs.dep.yml
@@ -1,13 +1,13 @@
 ---
 name: golang.org/x/sys/execabs
-version: v0.31.0
+version: v0.32.0
 type: go
 summary: Package execabs is a drop-in replacement for os/exec that requires PATH lookups
   to find absolute paths.
 homepage: https://pkg.go.dev/golang.org/x/sys/execabs
 license: other
 licenses:
-- sources: sys@v0.31.0/LICENSE
+- sources: sys@v0.32.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -36,7 +36,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: sys@v0.31.0/PATENTS
+- sources: sys@v0.32.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/sys/unix.dep.yml b/.licenses/go/golang.org/x/sys/unix.dep.yml
index 4d665e786ad..63734fd7e4f 100644
--- a/.licenses/go/golang.org/x/sys/unix.dep.yml
+++ b/.licenses/go/golang.org/x/sys/unix.dep.yml
@@ -1,12 +1,12 @@
 ---
 name: golang.org/x/sys/unix
-version: v0.31.0
+version: v0.32.0
 type: go
 summary: Package unix contains an interface to the low-level operating system primitives.
 homepage: https://pkg.go.dev/golang.org/x/sys/unix
 license: bsd-3-clause
 licenses:
-- sources: sys@v0.31.0/LICENSE
+- sources: sys@v0.32.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -35,7 +35,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: sys@v0.31.0/PATENTS
+- sources: sys@v0.32.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/term.dep.yml b/.licenses/go/golang.org/x/term.dep.yml
index 6fe4350ed28..a8c6b02a552 100644
--- a/.licenses/go/golang.org/x/term.dep.yml
+++ b/.licenses/go/golang.org/x/term.dep.yml
@@ -1,6 +1,6 @@
 ---
 name: golang.org/x/term
-version: v0.30.0
+version: v0.31.0
 type: go
 summary: Package term provides support functions for dealing with terminals, as commonly
   found on UNIX systems.
diff --git a/.licenses/go/golang.org/x/text/encoding.dep.yml b/.licenses/go/golang.org/x/text/encoding.dep.yml
index e783fb61b23..d72fdaa52d0 100644
--- a/.licenses/go/golang.org/x/text/encoding.dep.yml
+++ b/.licenses/go/golang.org/x/text/encoding.dep.yml
@@ -1,13 +1,13 @@
 ---
 name: golang.org/x/text/encoding
-version: v0.23.0
+version: v0.24.0
 type: go
 summary: Package encoding defines an interface for character encodings, such as Shift
   JIS and Windows 1252, that can convert to and from UTF-8.
 homepage: https://pkg.go.dev/golang.org/x/text/encoding
 license: other
 licenses:
-- sources: text@v0.23.0/LICENSE
+- sources: text@v0.24.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -36,7 +36,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: text@v0.23.0/PATENTS
+- sources: text@v0.24.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/text/encoding/internal.dep.yml b/.licenses/go/golang.org/x/text/encoding/internal.dep.yml
index 17ae73ae332..5866849a9bd 100644
--- a/.licenses/go/golang.org/x/text/encoding/internal.dep.yml
+++ b/.licenses/go/golang.org/x/text/encoding/internal.dep.yml
@@ -1,12 +1,12 @@
 ---
 name: golang.org/x/text/encoding/internal
-version: v0.23.0
+version: v0.24.0
 type: go
 summary: Package internal contains code that is shared among encoding implementations.
 homepage: https://pkg.go.dev/golang.org/x/text/encoding/internal
 license: other
 licenses:
-- sources: text@v0.23.0/LICENSE
+- sources: text@v0.24.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -35,7 +35,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: text@v0.23.0/PATENTS
+- sources: text@v0.24.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/text/encoding/internal/identifier.dep.yml b/.licenses/go/golang.org/x/text/encoding/internal/identifier.dep.yml
index 4342b78cfd7..dfd79d45d17 100644
--- a/.licenses/go/golang.org/x/text/encoding/internal/identifier.dep.yml
+++ b/.licenses/go/golang.org/x/text/encoding/internal/identifier.dep.yml
@@ -1,6 +1,6 @@
 ---
 name: golang.org/x/text/encoding/internal/identifier
-version: v0.23.0
+version: v0.24.0
 type: go
 summary: Package identifier defines the contract between implementations of Encoding
   and Index by defining identifiers that uniquely identify standardized coded character
@@ -10,7 +10,7 @@ summary: Package identifier defines the contract between implementations of Enco
 homepage: https://pkg.go.dev/golang.org/x/text/encoding/internal/identifier
 license: other
 licenses:
-- sources: text@v0.23.0/LICENSE
+- sources: text@v0.24.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -39,7 +39,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: text@v0.23.0/PATENTS
+- sources: text@v0.24.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/text/encoding/unicode.dep.yml b/.licenses/go/golang.org/x/text/encoding/unicode.dep.yml
index eec529f669f..5d614c25340 100644
--- a/.licenses/go/golang.org/x/text/encoding/unicode.dep.yml
+++ b/.licenses/go/golang.org/x/text/encoding/unicode.dep.yml
@@ -1,12 +1,12 @@
 ---
 name: golang.org/x/text/encoding/unicode
-version: v0.23.0
+version: v0.24.0
 type: go
 summary: Package unicode provides Unicode encodings such as UTF-16.
 homepage: https://pkg.go.dev/golang.org/x/text/encoding/unicode
 license: other
 licenses:
-- sources: text@v0.23.0/LICENSE
+- sources: text@v0.24.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -35,7 +35,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: text@v0.23.0/PATENTS
+- sources: text@v0.24.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/text/internal/utf8internal.dep.yml b/.licenses/go/golang.org/x/text/internal/utf8internal.dep.yml
index ffd95bbc52d..5a94ace3dac 100644
--- a/.licenses/go/golang.org/x/text/internal/utf8internal.dep.yml
+++ b/.licenses/go/golang.org/x/text/internal/utf8internal.dep.yml
@@ -1,12 +1,12 @@
 ---
 name: golang.org/x/text/internal/utf8internal
-version: v0.23.0
+version: v0.24.0
 type: go
 summary: Package utf8internal contains low-level utf8-related constants, tables, etc.
 homepage: https://pkg.go.dev/golang.org/x/text/internal/utf8internal
 license: other
 licenses:
-- sources: text@v0.23.0/LICENSE
+- sources: text@v0.24.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -35,7 +35,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: text@v0.23.0/PATENTS
+- sources: text@v0.24.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/.licenses/go/golang.org/x/text/runes.dep.yml b/.licenses/go/golang.org/x/text/runes.dep.yml
index 041123bb05c..faccf2bbd7a 100644
--- a/.licenses/go/golang.org/x/text/runes.dep.yml
+++ b/.licenses/go/golang.org/x/text/runes.dep.yml
@@ -1,12 +1,12 @@
 ---
 name: golang.org/x/text/runes
-version: v0.23.0
+version: v0.24.0
 type: go
 summary: Package runes provide transforms for UTF-8 encoded text.
 homepage: https://pkg.go.dev/golang.org/x/text/runes
 license: bsd-3-clause
 licenses:
-- sources: text@v0.23.0/LICENSE
+- sources: text@v0.24.0/LICENSE
   text: |
     Copyright 2009 The Go Authors.
 
@@ -35,7 +35,7 @@ licenses:
     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- sources: text@v0.23.0/PATENTS
+- sources: text@v0.24.0/PATENTS
   text: |
     Additional IP Rights Grant (Patents)
 
diff --git a/go.mod b/go.mod
index 604790e9e1e..51082fa93a1 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ replace github.com/mailru/easyjson => github.com/cmaglie/easyjson v0.8.1
 require (
 	fortio.org/safecast v1.0.0
 	github.com/ProtonMail/go-crypto v1.1.6
-	github.com/arduino/go-paths-helper v1.12.1
+	github.com/arduino/go-paths-helper v1.13.0
 	github.com/arduino/go-properties-orderedmap v1.8.1
 	github.com/arduino/go-serial-utils v0.1.2
 	github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b
@@ -40,9 +40,9 @@ require (
 	go.bug.st/f v0.4.0
 	go.bug.st/relaxed-semver v0.15.0
 	go.bug.st/testifyjson v1.3.0
-	golang.org/x/sys v0.31.0
-	golang.org/x/term v0.30.0
-	golang.org/x/text v0.23.0
+	golang.org/x/sys v0.32.0
+	golang.org/x/term v0.31.0
+	golang.org/x/text v0.24.0
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f
 	google.golang.org/grpc v1.71.1
 	google.golang.org/protobuf v1.36.6
diff --git a/go.sum b/go.sum
index d24ad016ad8..5675d8b1a35 100644
--- a/go.sum
+++ b/go.sum
@@ -10,8 +10,8 @@ github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQA
 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
 github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
-github.com/arduino/go-paths-helper v1.12.1 h1:WkxiVUxBjKWlLMiMuYy8DcmVrkxdP7aKxQOAq7r2lVM=
-github.com/arduino/go-paths-helper v1.12.1/go.mod h1:jcpW4wr0u69GlXhTYydsdsqAjLaYK5n7oWHfKqOG6LM=
+github.com/arduino/go-paths-helper v1.13.0 h1:HIkgg8ChPw1QPNHkB5bQSs+geTj74vf6TFgVhm/9mmw=
+github.com/arduino/go-paths-helper v1.13.0/go.mod h1:jcpW4wr0u69GlXhTYydsdsqAjLaYK5n7oWHfKqOG6LM=
 github.com/arduino/go-properties-orderedmap v1.8.1 h1:nU5S6cXPwMoxZs4ORw61wPTALNfriIduvNB4cxTmNYM=
 github.com/arduino/go-properties-orderedmap v1.8.1/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
 github.com/arduino/go-serial-utils v0.1.2 h1:MRFwME4w/uaVkJ1R+wzz4KSbI9cF9IDVrYorazvjpTk=
@@ -235,14 +235,14 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
+golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
diff --git a/internal/arduino/builder/internal/preprocessor/gcc.go b/internal/arduino/builder/internal/preprocessor/gcc.go
index cbf156dfae6..c2398163e39 100644
--- a/internal/arduino/builder/internal/preprocessor/gcc.go
+++ b/internal/arduino/builder/internal/preprocessor/gcc.go
@@ -16,6 +16,7 @@
 package preprocessor
 
 import (
+	"bytes"
 	"context"
 	"errors"
 	"fmt"
@@ -77,10 +78,42 @@ func GCC(
 	if err != nil {
 		return Result{}, err
 	}
-	stdout, stderr, err := proc.RunAndCaptureOutput(ctx)
 
-	// Append gcc arguments to stdout
-	stdout = append([]byte(fmt.Sprintln(strings.Join(args, " "))), stdout...)
+	stdout := bytes.NewBuffer(nil)
+	stderr := bytes.NewBuffer(nil)
 
-	return Result{args: proc.GetArgs(), stdout: stdout, stderr: stderr}, err
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+	count := 0
+	stderrLimited := writerFunc(func(p []byte) (int, error) {
+		// Limit the size of the stderr buffer to 100KB
+		n, err := stderr.Write(p)
+		count += n
+		if count > 100*1024 {
+			cancel()
+			fmt.Fprintln(stderr, i18n.Tr("Compiler error output has been truncated."))
+		}
+		return n, err
+	})
+
+	proc.RedirectStdoutTo(stdout)
+	proc.RedirectStderrTo(stderrLimited)
+
+	// Append gcc arguments to stdout before running the command
+	fmt.Fprintln(stdout, strings.Join(args, " "))
+
+	if err := proc.Start(); err != nil {
+		return Result{}, err
+	}
+
+	// Wait for the process to finish
+	err = proc.WaitWithinContext(ctx)
+
+	return Result{args: proc.GetArgs(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
+}
+
+type writerFunc func(p []byte) (n int, err error)
+
+func (f writerFunc) Write(p []byte) (n int, err error) {
+	return f(p)
 }
diff --git a/internal/integrationtest/arduino-cli.go b/internal/integrationtest/arduino-cli.go
index f035e792f55..60b85260b03 100644
--- a/internal/integrationtest/arduino-cli.go
+++ b/internal/integrationtest/arduino-cli.go
@@ -22,6 +22,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"maps"
 	"os"
 	"runtime"
 	"strings"
@@ -190,12 +191,16 @@ func (cli *ArduinoCLI) Run(args ...string) ([]byte, []byte, error) {
 	return cli.RunWithCustomEnv(cli.cliEnvVars, args...)
 }
 
+// RunWithContext executes the given arduino-cli command with the given context and returns the output.
+// If the context is canceled, the command is killed.
+func (cli *ArduinoCLI) RunWithContext(ctx context.Context, args ...string) ([]byte, []byte, error) {
+	return cli.RunWithCustomEnvContext(ctx, cli.cliEnvVars, args...)
+}
+
 // GetDefaultEnv returns a copy of the default execution env used with the Run method.
 func (cli *ArduinoCLI) GetDefaultEnv() map[string]string {
 	res := map[string]string{}
-	for k, v := range cli.cliEnvVars {
-		res[k] = v
-	}
+	maps.Copy(cli.cliEnvVars, res)
 	return res
 }
 
@@ -324,8 +329,13 @@ func (cli *ArduinoCLI) InstallMockedAvrdude(t *testing.T) {
 
 // RunWithCustomEnv executes the given arduino-cli command with the given custom env and returns the output.
 func (cli *ArduinoCLI) RunWithCustomEnv(env map[string]string, args ...string) ([]byte, []byte, error) {
+	return cli.RunWithCustomEnvContext(context.Background(), env, args...)
+}
+
+// RunWithCustomEnv executes the given arduino-cli command with the given custom env and returns the output.
+func (cli *ArduinoCLI) RunWithCustomEnvContext(ctx context.Context, env map[string]string, args ...string) ([]byte, []byte, error) {
 	var stdoutBuf, stderrBuf bytes.Buffer
-	err := cli.run(&stdoutBuf, &stderrBuf, nil, env, args...)
+	err := cli.run(ctx, &stdoutBuf, &stderrBuf, nil, env, args...)
 
 	errBuf := stderrBuf.Bytes()
 	cli.t.NotContains(string(errBuf), "panic: runtime error:", "arduino-cli panicked")
@@ -336,7 +346,7 @@ func (cli *ArduinoCLI) RunWithCustomEnv(env map[string]string, args ...string) (
 // RunWithCustomInput executes the given arduino-cli command pushing the given input stream and returns the output.
 func (cli *ArduinoCLI) RunWithCustomInput(in io.Reader, args ...string) ([]byte, []byte, error) {
 	var stdoutBuf, stderrBuf bytes.Buffer
-	err := cli.run(&stdoutBuf, &stderrBuf, in, cli.cliEnvVars, args...)
+	err := cli.run(context.Background(), &stdoutBuf, &stderrBuf, in, cli.cliEnvVars, args...)
 
 	errBuf := stderrBuf.Bytes()
 	cli.t.NotContains(string(errBuf), "panic: runtime error:", "arduino-cli panicked")
@@ -344,7 +354,7 @@ func (cli *ArduinoCLI) RunWithCustomInput(in io.Reader, args ...string) ([]byte,
 	return stdoutBuf.Bytes(), errBuf, err
 }
 
-func (cli *ArduinoCLI) run(stdoutBuff, stderrBuff io.Writer, stdinBuff io.Reader, env map[string]string, args ...string) error {
+func (cli *ArduinoCLI) run(ctx context.Context, stdoutBuff, stderrBuff io.Writer, stdinBuff io.Reader, env map[string]string, args ...string) error {
 	if cli.cliConfigPath != nil {
 		args = append([]string{"--config-file", cli.cliConfigPath.String()}, args...)
 	}
@@ -402,8 +412,8 @@ func (cli *ArduinoCLI) run(stdoutBuff, stderrBuff io.Writer, stdinBuff io.Reader
 			}
 		}()
 	}
+	cliErr := cliProc.WaitWithinContext(ctx)
 	wg.Wait()
-	cliErr := cliProc.Wait()
 	fmt.Fprintln(terminalOut, color.HiBlackString("<<< Run completed (err = %v)", cliErr))
 
 	return cliErr
diff --git a/internal/integrationtest/compile_5/recusive_include_test.go b/internal/integrationtest/compile_5/recusive_include_test.go
new file mode 100644
index 00000000000..59a3c421c7f
--- /dev/null
+++ b/internal/integrationtest/compile_5/recusive_include_test.go
@@ -0,0 +1,44 @@
+// This file is part of arduino-cli.
+//
+// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
+//
+// This software is released under the GNU General Public License version 3,
+// which covers the main part of arduino-cli.
+// The terms of this license can be found at:
+// https://www.gnu.org/licenses/gpl-3.0.en.html
+//
+// You can be released from the requirements of the above licenses by purchasing
+// a commercial license. Buying such a license is mandatory if you want to
+// modify or otherwise use the software for commercial activities involving the
+// Arduino software without disclosing the source code of your own applications.
+// To purchase a commercial license, send an email to license@arduino.cc.
+
+package compile_test
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/arduino/arduino-cli/internal/integrationtest"
+	"github.com/arduino/go-paths-helper"
+	"github.com/stretchr/testify/require"
+)
+
+func TestCompileWithInfiniteMultipleIncludeRecursion(t *testing.T) {
+	env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
+	t.Cleanup(env.CleanUp)
+
+	// Install Arduino AVR Boards
+	_, _, err := cli.Run("core", "install", "arduino:avr@1.8.6")
+	require.NoError(t, err)
+
+	sketch, err := paths.New("testdata", "SketchWithRecursiveIncludes").Abs()
+	require.NoError(t, err)
+
+	// Time-limited test to prevent OOM
+	ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
+	t.Cleanup(cancel)
+	_, _, _ = cli.RunWithContext(ctx, "compile", "-b", "arduino:avr:uno", sketch.String())
+	require.NotErrorIs(t, ctx.Err(), context.DeadlineExceeded, "compilation should not hang")
+}
diff --git a/internal/integrationtest/compile_5/testdata/SketchWithRecursiveIncludes/SketchWithRecursiveIncludes.ino b/internal/integrationtest/compile_5/testdata/SketchWithRecursiveIncludes/SketchWithRecursiveIncludes.ino
new file mode 100644
index 00000000000..2243de1baf9
--- /dev/null
+++ b/internal/integrationtest/compile_5/testdata/SketchWithRecursiveIncludes/SketchWithRecursiveIncludes.ino
@@ -0,0 +1 @@
+#include "a.h"
diff --git a/internal/integrationtest/compile_5/testdata/SketchWithRecursiveIncludes/a.h b/internal/integrationtest/compile_5/testdata/SketchWithRecursiveIncludes/a.h
new file mode 100644
index 00000000000..7c626e5beb0
--- /dev/null
+++ b/internal/integrationtest/compile_5/testdata/SketchWithRecursiveIncludes/a.h
@@ -0,0 +1,2 @@
+#include "a.h"
+#include "a.h"