Summary
A developer inadvertently deleted their global Haskell Stack configuration directory (~/.stack) while troubleshooting an unrelated issue. This action destroyed the integration layer between stack and ghcup. While stack can still function, it lost the ability to delegate GHC (Glasgow Haskell Compiler) version management to ghcup. This results in stack attempting to download its own isolated versions of GHC, bypassing the centralized management provided by ghcup and breaking the seamless link with Haskell Language Server (HLS) binaries.
Root Cause
The failure stems from the loss of the config.yaml file located within the ~/.stack directory. This file contains the specific configuration directives required for the integration. Specifically:
- Configuration Erasure: The deletion of
~/.stackremoved the instructions that tellstackhow to find and use the GHC toolchains managed byghcup. - Loss of Path Mapping: Without the configuration,
stackreverts to its default behavior, which is to manage its own internal, sandboxed GHC installations. - Integration Decoupling: The mechanism that allows
stackto “borrow” GHC versions from theghcupinstallation path was lost.
Why This Happens in Real Systems
In complex development environments, stateful configuration directories are often treated as “disposable” by engineers trying to perform a “clean slate” reset. This happens because:
- Undocumented Dependencies: Users often forget that a tool (like
stack) can be configured to depend on the state or settings of another tool (ghcup). - Implicit Coupling: The integration isn’t a hard system link but a soft configuration link. Deleting the config doesn’t break the binaries, but it breaks the orchestration logic between them.
- Debugging Side-Effects: When troubleshooting a deep issue, engineers often perform “scorched earth” tactics—deleting home directory dotfiles—without realizing the downstream impact on tool interop.
Real-World Impact
The impact of losing this integration is primarily related to developer velocity and system consistency:
- Disk Bloat:
stackwill begin downloading multiple copies of GHC into its own internal storage, duplicating the space already used byghcup. - Toolchain Mismatch: The Haskell Language Server (HLS) may struggle to find the correct GHC version if
stackis using a different binary than the oneghcuphas prepared for the IDE. - Build Inconsistency: Developers might end up with different compiler versions across different projects, leading to the “it works on my machine” problem when CI/CD uses a different managed toolchain.
Example or Code
To recover the integration, you must manually re-inject the ghcup path configuration into a new config.yaml file.
# ~/.stack/config.yaml
install-ghc: true
compiler: ghc-9.4.5
system-ghc: true
Note: The system-ghc: true flag is the critical component that instructs stack to look for the compiler in the system PATH (where ghcup places it) rather than downloading its own.
How Senior Engineers Fix It
A senior engineer approaches this by treating the configuration as reproducible state rather than a manual chore.
- Re-establishing the Link: Instead of just reinstalling tools, they explicitly configure the
system-ghcflag to ensurestackhonors the existing environment. - Environment Auditing: They verify the
PATHto ensureghcupbinaries are prioritized, allowingstackto see the managed GHCs immediately. - Configuration as Code: To prevent this in the future, a senior engineer would store their dotfiles (like
.stack/config.yaml) in a Git repository (dotfiles repo), making recovery as simple as agit clone.
Why Juniors Miss It
Junior engineers often miss the systemic nature of the problem:
- Focus on Binaries, Not Config: They might try to fix the issue by reinstalling
stackorghcup, not realizing the binaries are fine—it is the metadata that is missing. - Symptom-Based Troubleshooting: They see “GHC not found” and assume the compiler is gone, rather than realizing the pointer to the compiler was deleted.
- Lack of State Awareness: They treat the
~/.stackdirectory as a cache (disposable) rather than a configuration store (persistent).