Summary
A deployment pipeline failed during a structural refactoring of the automation repository. A PowerShell script that previously relied on relative pathing (.\) to execute a sibling script broke immediately when the directory structure changed. The failure occurred because the execution context was tied to the Current Working Directory (CWD) rather than the Script Location, leading to a PathNotFoundException.
Root Cause
The fundamental issue is the misuse of relative paths in a non-deterministic execution environment.
- Relative Path Ambiguity: The notation
.\does not mean “the directory where this script lives”; it means “the directory the user is currently standing in when they run the command.” - Context Sensitivity: When the script was moved to a sibling directory, the developer attempted to use
..\to climb the tree. However, if the script is invoked from a different shell location (e.g., via a CI/CD agent or a scheduled task), the relative jump leads to an invalid path. - Refactoring Blindness: The code assumed the filesystem topology would always match the logical execution flow.
Why This Happens in Real Systems
In production environments, scripts are rarely run by a human typing into a terminal. They are executed by:
- CI/CD Runners (Jenkins, GitHub Actions, GitLab): These often initialize the shell in a workspace root, not the script’s subfolder.
- Scheduled Tasks (Cron, Windows Task Scheduler): These frequently execute with a default “Start In” directory of
C:\Windows\System32or the user’s home profile. - Orchestrators (Kubernetes, Ansible): These tools abstract the filesystem, making “relative jumps” highly unpredictable.
Real-World Impact
- Deployment Failures: Automated pipelines halt mid-way, potentially leaving infrastructure in a partially provisioned/dirty state.
- Brittle Automation: Maintenance becomes a nightmare; every time a folder is reorganized for better readability, dozens of scripts break.
- Increased MTTR (Mean Time To Recovery): On-call engineers waste time debugging pathing issues instead of actual logic errors during an outage.
Example or Code
# THE WRONG WAY: Fragile and context-dependent
.\..\siblingfolder\mysuperscript.ps1 -param1 bla
# THE RIGHT WAY: Deterministic and location-aware
$ScriptRoot = Split-Path -Parent $PSScriptRoot
$SiblingScript = Join-Path -Path $ScriptRoot -ChildPath "siblingfolder\mysuperscript.ps1"
& $SiblingScript -param1 bla -param2 blabla -param3 blob
How Senior Engineers Fix It
Senior engineers design for Idempotency and Determinism. Instead of assuming where the shell is, they derive the path from the script’s own metadata.
- Use
$PSScriptRoot: This is a built-in automatic variable that always points to the directory containing the currently executing script, regardless of the CWD. - Path Normalization: Always use
Join-Pathto construct paths. This prevents errors caused by missing or double backslashes (\\). - Absolute Path Resolution: Transform relative logic into absolute paths at the very start of the script execution.
- Defensive Programming: Add a check to see if the target script exists before attempting to execute it, providing a clear error message if it does not.
Why Juniors Miss It
- Local Success Bias: The code works perfectly on their local machine because they manually
cdinto the directory before running it. - Mental Model Error: They view the script as a “file in a folder” rather than a “process in an environment.”
- Over-reliance on Syntax: They focus on whether the
..\syntax is valid (it is) rather than whether the logic is robust (it isn’t).