Date: 2026-03-17
Related Issues:
atmos describe affectedfails withError: failed to find import(GitHub issue #2183)terraform-provider-utilsv1.32.0+ fails withfailed to find importondata.utils_component_configwhenATMOS_BASE_PATHenv var is set as a relative path- Both issues affect Atmos versions > v1.200.0
Affected Atmos Versions: v1.202.0+ (since the base path behavior change to git root discovery)
Severity: High — blocks describe affected, terraform plan with utils provider, and CI/CD pipelines
Two related failures surface the same error:
When using terraform-provider-utils v1.32.0+ (which depends on Atmos v1.207.0+), Terraform plans
fail with:
│ Error: failed to find import
│
│ with module.account_map.data.utils_component_config.config[0],
│ on .terraform/modules/account_map/modules/remote-state/main.tf line 1
The user sets the ATMOS_BASE_PATH environment variable to a relative path on the CI/CD worker
(e.g., ATMOS_BASE_PATH=.terraform/modules/monorepo). This env var is read by Viper during config
loading and stored in atmosConfig.BasePath. With Atmos v1.200.0 and earlier, this resolved relative
to CWD. With v1.202.0+, resolveAbsolutePath() routes simple relative paths (those not starting with
./ or ../) through git root discovery, producing a wrong absolute path.
The base path can also be set via:
- The
atmos_base_pathparameter on thedata.utils_component_configTerraform resource (passed to Atmos viaconfigAndStacksInfo.AtmosBasePath) - The
--base-pathCLI flag
Running atmos describe affected or atmos tf plan --affected fails with:
Error: failed to find import
This happens during stack processing when the stacks base path is computed incorrectly due to git root
discovery interfering with the configured base_path.
The error "failed to find import" provides no context about which import failed or what path was
searched. Users cannot diagnose whether it's a configuration issue, a missing file, or a path
resolution bug.
The resolveAbsolutePath() function in pkg/config/config.go was updated in v1.202.0 to use git
root discovery for resolving relative paths. The resolution logic is:
- If path is absolute → return as-is
- If path starts with
./or../→ resolve relative toatmos.yamllocation - If path is empty or a simple relative path (e.g.,
"stacks",".terraform/modules/monorepo") → resolve relative to git root, falling back toatmos.yamldir, then CWD
Step 3 is the problem. Simple relative paths like .terraform/modules/monorepo are treated as
git-root-relative, but they are actually CWD-relative (set by the user via --base-path,
ATMOS_BASE_PATH env var, or the atmos_base_path provider parameter).
The terraform-provider-utils runs inside a Terraform provider plugin process. The working directory
is the component directory (e.g., components/terraform/iam-delegated-roles/). The user sets
ATMOS_BASE_PATH=.terraform/modules/monorepo as an environment variable on the CI/CD worker. Viper
reads this env var and stores it in atmosConfig.BasePath. Then resolveAbsolutePath() routes it
through git root discovery:
- CWD:
/project/components/terraform/iam-delegated-roles/ - Git root:
/project/(the monorepo root) resolveAbsolutePath(".terraform/modules/monorepo")→filepath.Join("/project/", ".terraform/modules/monorepo")→/project/.terraform/modules/monorepo— WRONG- Correct path:
/project/components/terraform/iam-delegated-roles/.terraform/modules/monorepo
The stacks directory doesn't exist at the git-root-relative path, so GetGlobMatches() returns
ErrFailedToFindImport.
describe affected calls ExecuteDescribeStacks which calls InitCliConfig →
AtmosConfigAbsolutePaths. If base_path in atmos.yaml is empty (""), resolveAbsolutePath("")
returns the git root. In most cases this is correct, but in CI environments where the working
directory structure differs from expectations (e.g., GitHub Actions with custom checkout paths), the
git root may not be the expected base path.
Additionally, FindAllStackConfigsInPathsForStack (in pkg/config/utils.go:55-57) returns the raw
ErrFailedToFindImport without wrapping it with context about which path or pattern failed.
There are two implementations of GetGlobMatches:
pkg/utils/glob_utils.go(used by stack processor) — treatsmatches == nilasErrFailedToFindImportpkg/filesystem/glob.go(newer) — treatsmatches == nilas empty result (not an error), only returnsErrFailedToFindImportwhen the base directory doesn't exist
The stack processor uses the old implementation (via u.GetGlobMatches), which returns an error on
zero matches even when the directory exists but no files match the pattern.
When BasePath is explicitly provided by the user (via --base-path, ATMOS_BASE_PATH env var, or
the atmos_base_path parameter in terraform-provider-utils), and it is a "simple relative" path
(not starting with ., .., ./, or ../), convert it to absolute immediately using
filepath.Abs() (which resolves relative to CWD).
File: pkg/config/config.go
In InitCliConfig, after processAtmosConfigs():
- Check
configAndStacksInfo.AtmosBasePathfirst (highest priority — provider/CLI struct field) - Fall back to
os.Getenv("ATMOS_BASE_PATH")(env var) - If the path is a simple relative path, convert to absolute via
filepath.Abs()(CWD-relative) - If the path starts with
.or.., leave it forresolveAbsolutePath()to handle (config-file-relative — preserves existing behavior forATMOS_BASE_PATH=".")
This happens before AtmosConfigAbsolutePaths runs, so resolveAbsolutePath() sees an absolute
path and returns it as-is, bypassing git root discovery entirely.
Wrap ErrFailedToFindImport with context wherever it surfaces, using the error builder pattern:
- In
pkg/config/utils.go— bothFindAllStackConfigsInPathsForStackandFindAllStackConfigsInPathsnow useerrUtils.Build(err)with actionable hints and context (pattern, stacks_base_path) - In
pkg/utils/glob_utils.go— added actionable hints about checkingbase_pathandstacks.base_pathinatmos.yaml, and aboutATMOS_BASE_PATHenv var
The old pkg/utils/glob_utils.go:GetGlobMatches treats zero matches as an error. The newer
pkg/filesystem/glob.go:GetGlobMatches correctly treats zero matches as empty results. The stack
processor should migrate to the newer implementation to avoid false errors. This is deferred to a
separate PR to minimize the blast radius of this fix.
| File | Change |
|---|---|
pkg/config/config.go |
Convert simple relative base paths to absolute (CWD-relative) in InitCliConfig |
pkg/config/utils.go |
Wrap errors with builder pattern, actionable hints, and context |
pkg/utils/glob_utils.go |
Add actionable hints to GetGlobMatches error |
pkg/config/base_path_resolution_test.go |
Tests for base path resolution and error wrapping |
A key concern is that these fixes do not break the "run Atmos from any subdirectory" feature introduced in v1.202.0 via git root discovery. Analysis confirms all code paths remain intact:
When a user runs atmos terraform plan vpc -s dev from a subdirectory (e.g.,
components/terraform/vpc/), the flow is:
SearchConfigFile()walks up from CWD to findatmos.yaml(e.g., at/project/atmos.yaml)resolveAbsolutePath()resolvesstacks.base_path(typically"stacks") relative to git roottryResolveWithGitRoot()joins git root + path →/project/stacks— this directory exists
Fix 1 (struct field resolution): resolveSimpleRelativeBasePath() only runs on
configAndStacksInfo.AtmosBasePath — the struct field set by --base-path flag or the
atmos_base_path provider parameter. It does NOT affect atmosConfig.BasePath loaded from
atmos.yaml or the default empty value. Normal Atmos CLI usage doesn't set AtmosBasePath.
Fix 2 (os.Stat fallback in tryResolveWithGitRoot): The added os.Stat check validates
the git-root-joined path exists before returning it. For normal projects:
resolveAbsolutePath("stacks")→tryResolveWithGitRoot("stacks")→filepath.Join(gitRoot, "stacks")→/project/stacks— exists → returned ✅resolveAbsolutePath("")→ returnsgitRootdirectly at line 315 (beforeos.Stat) ✅resolveAbsolutePath("./foo")→ caught byisExplicitRelative→ never reachesos.Stat✅
The os.Stat fallback only triggers when a simple relative path does NOT exist at the git root
but DOES exist relative to CWD — precisely the ATMOS_BASE_PATH=.terraform/modules/monorepo
scenario.
The following existing integration tests verify "run from any directory" behavior:
describe_component_from_nested_dir_discovers_atmos.yaml_in_parent— runsdescribe componentfromtests/fixtures/scenarios/complete/components/terraform/weather/✅terraform_plan_from_nested_dir_discovers_atmos.yaml_in_parent— runsterraform planfrom a nested component directory ✅terraform_plan_with_current_directory_(.)— runs withbase_path: .✅
- Simple relative
base_pathvalues (via--base-pathflag,ATMOS_BASE_PATHenv var, oratmos_base_pathprovider parameter) will resolve relative to CWD instead of git root — this restores the pre-v1.202.0 behavior for explicitly set paths - Config-relative paths (
".","./foo","../foo") continue to resolve relative toatmos.yamllocation — no change - Default/empty
base_pathcontinues to use git root discovery (the v1.202.0 behavior) - Error messages become more informative with actionable hints — no behavioral change, just better diagnostics
ATMOS_GIT_ROOT_BASEPATH=falsecontinues to work as a full opt-out of git root discovery
TestInitCliConfig_ExplicitBasePath_ResolvesRelativeToCWD— verify struct field base path resolves to CWDTestInitCliConfig_ExplicitBasePath_AbsolutePassedThrough— verify absolute path is unchangedTestInitCliConfig_EmptyBasePath_DefaultsToAbsolute— verify empty base path uses default resolutionTestInitCliConfig_EnvVarBasePath_ResolvesRelativeToCWD— verifyATMOS_BASE_PATHenv var with relative path resolves to CWD (Tyler's exact scenario)TestTryResolveWithGitRoot_ExistingPathAtGitRoot— verifyos.Statfallback doesn't break normal git root discovery (stacks dir at git root found correctly)TestResolveSimpleRelativeBasePath— table-driven test for the helper function covering empty, absolute, dot-relative, and simple relative pathsTestFindAllStackConfigsInPathsForStack_ErrorWrapping— verify error wrapsErrFailedToFindImportTestFindAllStackConfigsInPaths_ErrorWrapping— verify error wrapping in non-stack variantTestDotPathResolvesRelativeToConfigDir— verifyATMOS_BASE_PATH="."still resolves to config dir (regression test)
describe_component_from_nested_dir_discovers_atmos.yaml_in_parent— subdirectory execution ✅terraform_plan_from_nested_dir_discovers_atmos.yaml_in_parent— nested dir plan ✅terraform_plan_with_current_directory_(.)— base_path=.✅- Verify
describe affectedworks with defaultbase_path: "" - Verify
describe affectedworks with explicitbase_path: "." - Verify terraform-provider-utils pattern with relative
ATMOS_BASE_PATHenv var
- Test with
ATMOS_GIT_ROOT_BASEPATH=falseto verify opt-out still works