Repository: gruntwork-io/terragrunt (language: go)

Issue:
The system cannot find the path specified
**Describe the bug**
With latest version (0.53.3) it seems that terragrunt is not able to complete any of the run-all commands more than once with the same cache.

I have a root directory with multiple subdirectories representing different environments and modules for deployment. Each subdirectory contains a terragrunt.hcl file, like this:
-live
--db_to_deltalake
---all_purpose_cluster
----terragrunt.hcl
---mssql
----terragrunt.hcl
---oracle
----terragrunt.hcl

When I run a terragrunt run-all plan the first run works as intended and generates cache. If I want to run terragrunt run-all plan again or run another command (for ex. terragrunt run-all apply or terragrunt run-all destroy) the command fails with the following error:
time=2023-11-15T18:07:29+02:00 level=error msg=Error processing module at 
[...]/live/db_to_deltalake/mssql/.terragrunt-cache/49rvKcmzroHbYFyuIsDxTOzlmag/all_purpose_cluster/terragrunt.hcl'. 
How this module was found: dependency of module at 
'[...]/Terragrunt/live/db_to_deltalake/mssql/.terragrunt-cache/49rvKcmzroHbYFyuIsDxTOzlmag/Up1v2-e2eN7_VINYorWqZObDNvk'. 
Underlying error: Error reading file at path 
[...]/Terragrunt/live/db_to_deltalake/mssql/.terragrunt-cache/49rvKcmzroHbYFyuIsDxTOzlmag/all_purpose_cluster/terragrunt.hcl: open 
[...]/Terragrunt/live/db_to_deltalake/mssql/.terragrunt-cache/49rvKcmzroHbYFyuIsDxTOzlmag/all_purpose_cluster/terragrunt.hcl: The system cannot find the path specified.
time=2023-11-15T18:07:29+02:00 level=error msg=Unable to determine underlying exit code, so Terragrunt will exit with error code 1

**To Reproduce**
Run in sequence the following commands:

terragrunt run-all plan
terragrunt run-all apply

or

terragrunt run-all apply
terragrunt run-all destroy


FYI: When I downgraded the version of Terragrunt to v0.50.14, the commands work as expected. I decided to choose exactly this version to downgrade as reading bug #2778. It seems they are related.

```hcl
// paste code snippets here
```

**Expected behavior**
To be able to execute run-all commands in a sequence using the same cache.

**Nice to have**
- [ ] Terminal output
- [ ] Screenshots

**Versions**
- Terragrunt version: v0.53.2
- Terraform version: v1.6.4
- Environment details (Ubuntu 20.04, Windows 10, etc.): Windows 11

**Additional context**
Add any other context about the problem here.


Discussion / hints:
I get the same issue (terragrunt  v0.53.0)
This bug appeared first in `v0.50.15`
Any updates on this? Issue is still present on the latest version (as of today): `v0.54.8`
Hello,
will be helpful to share an example repository where the issue happens,
I tried to reproduce in https://github.com/denis256/terragrunt-tests/tree/master/issue-2794 but all `run-all` commands pass without errors
Hello @denis256,

I've set up a repository to demonstrate the issue we're encountering, which mirrors a simplified version of our production code. You can find the repository here: https://github.com/deBFM/terragrunt-issue-2794.
Within this repository, I've outlined the steps necessary to reproduce the issue.

Thank you for looking into this issue.
Hi,
will take a look on Windows, tests on Ubuntu 22.04 worked without any issue 

Happens on windows environment
![image](https://github.com/gruntwork-io/terragrunt/assets/10694338/853830f3-e306-4dbf-bd80-429b39443d0d)


Fix patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 4d6ee58890..9e03dbb3dc 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -72,6 +72,10 @@ jobs:
             # means of reproducing Terragrunt issues that only occur on that platform
             go test -v ./... -run TestWindowsTerragruntSourceMapDebug -timeout 45m
             go test -v ./... -run TestWindowsTflintIsInvoked -timeout 45m
+            go test -v ./... -run TestCanonicalPath -timeout 45m
+            go test -v ./... -run TestJoinTerraformModulePath -timeout 45m
+            go test -v ./... -run TestIncludeInCopy -timeout 45m
+            go test -tags windows -v -un -run TestWindows ./test -timeout 45m
       - run:
           name: Run go tofu tests
           shell: powershell.exe
@@ -83,6 +87,10 @@ jobs:
             # means of reproducing Terragrunt issues that only occur on that platform
             go test -v ./... -run TestWindowsTerragruntSourceMapDebug -timeout 45m
             go test -v ./... -run TestWindowsTflintIsInvoked -timeout 45m
+            go test -v ./... -run TestCanonicalPath -timeout 45m
+            go test -v ./... -run TestJoinTerraformModulePath -timeout 45m
+            go test -v ./... -run TestIncludeInCopy -timeout 45m
+            go test -tags windows -v -un -run TestWindows ./test -timeout 45m
   # We're running unit tests separately from integration tests - with no parallelization.
   # With heavy parallelization coupled with re-use of test fixtures we've witnessed slight
   # instability with the tests. The unit tests are fast to execute, so there is negligible
@@ -225,7 +233,6 @@ jobs:
           path: logs
       - store_test_results:
           path: logs
-
   build:
     resource_class: large
     <<: *defaults
diff --git a/util/file.go b/util/file.go
index 2988bd2551..a76205744b 100644
--- a/util/file.go
+++ b/util/file.go
@@ -405,7 +405,7 @@ func JoinPath(elem ...string) string {
 // E.g. "/foo/bar/boo.txt" -> ["", "foo", "bar", "boo.txt"]
 // Notice that if path is absolute the resulting list will begin with an empty string.
 func SplitPath(path string) []string {
-	return strings.Split(CleanPath(path), string(filepath.Separator))
+	return strings.Split(CleanPath(path), filepath.ToSlash(string(filepath.Separator)))
 }
 
 // Use this function when cleaning paths to ensure the returned path uses / as the path separator to improve cross-platform compatibility


Test patch:
diff --git a/config/config_test.go b/config/config_test.go
index 0fe9b0959c..07df53745e 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -649,8 +649,8 @@ func TestParseTerragruntConfigTwoLevels(t *testing.T) {
 	_, actualErr := ParseConfigString(ctx, configPath, config, nil)
 	expectedErr := TooManyLevelsOfInheritanceError{
 		ConfigPath:             configPath,
-		FirstLevelIncludePath:  absPath(t, "../test/fixture-parent-folders/multiple-terragrunt-in-parents/child/"+DefaultTerragruntConfigPath),
-		SecondLevelIncludePath: absPath(t, "../test/fixture-parent-folders/multiple-terragrunt-in-parents/child/"+DefaultTerragruntConfigPath),
+		FirstLevelIncludePath:  filepath.ToSlash(absPath(t, "../test/fixture-parent-folders/multiple-terragrunt-in-parents/child/"+DefaultTerragruntConfigPath)),
+		SecondLevelIncludePath: filepath.ToSlash(absPath(t, "../test/fixture-parent-folders/multiple-terragrunt-in-parents/child/"+DefaultTerragruntConfigPath)),
 	}
 	assert.True(t, errors.IsError(actualErr, expectedErr), "Expected error %v but got %v", expectedErr, actualErr)
 }
diff --git a/test/fixture-find-parent/app/main.tf b/test/fixture-find-parent/app/main.tf
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/fixture-find-parent/app/terragrunt.hcl b/test/fixture-find-parent/app/terragrunt.hcl
new file mode 100644
index 0000000000..70a260c31c
--- /dev/null
+++ b/test/fixture-find-parent/app/terragrunt.hcl
@@ -0,0 +1,6 @@
+terraform {
+  source = "."
+}
+include "root" {
+  path = find_in_parent_folders()
+}
\ No newline at end of file
diff --git a/test/fixture-find-parent/terragrunt.hcl b/test/fixture-find-parent/terragrunt.hcl
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/integration_windows_test.go b/test/integration_windows_test.go
index 8809ab1147..91a1ba99bf 100644
--- a/test/integration_windows_test.go
+++ b/test/integration_windows_test.go
@@ -20,6 +20,7 @@ import (
 const (
 	TEST_FIXTURE_LOCAL_RELATIVE_ARGS_WINDOWS_DOWNLOAD_PATH = "fixture-download/local-relative-extra-args-windows"
 	TEST_FIXTURE_MANIFEST_REMOVAL                          = "fixture-manifest-removal"
+	TEST_FIXTURE_FIND_PARENT                               = "fixture-find-parent"
 )
 
 func TestWindowsLocalWithRelativeExtraArgsWindows(t *testing.T) {
@@ -131,6 +132,17 @@ func fileInfo(path, fileName string) (*os.FileInfo, error) {
 	return fileInfo, err
 }
 
+func TestWindowsFindParent(t *testing.T) {
+	t.Parallel()
+
+	cleanupTerraformFolder(t, TEST_FIXTURE_FIND_PARENT)
+
+	runTerragrunt(t, fmt.Sprintf("terragrunt run-all plan --terragrunt-non-interactive --terragrunt-working-dir %s", TEST_FIXTURE_FIND_PARENT))
+
+	// second run shouldn't fail with find_in_parent_folders() issue
+	runTerragrunt(t, fmt.Sprintf("terragrunt run-all apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", TEST_FIXTURE_FIND_PARENT))
+}
+
 func copyEnvironmentToPath(t *testing.T, environmentPath, targetPath string) {
 	if err := os.MkdirAll(targetPath, 0777); err != nil {
 		t.Fatalf("Failed to create temp dir %s due to error %v", targetPath, err)
diff --git a/util/file_test.go b/util/file_test.go
index 0cb7960683..2e032c4356 100644
--- a/util/file_test.go
+++ b/util/file_test.go
@@ -77,7 +77,7 @@ func TestGlobCanonicalPath(t *testing.T) {
 	expectedHelper := func(path string) string {
 		basePath, err := filepath.Abs(basePath)
 		assert.NoError(t, err)
-		return filepath.Join(basePath, path)
+		return filepath.ToSlash(filepath.Join(basePath, path))
 	}
 
 	testCases := []struct {