Describe the bug
When running git commit from a linked git worktree (git worktree add ...) on a stack that references modules, the commit fails at tree-build time with:
error: invalid object 100644 <oid> for '<some-upstream-module-file>'
error: Error building trees
even though every pre-commit hook reports Passed.
Root cause: terraform_validate invokes tofu init -backend=false, which shells out to git clone <module-source> for each module { source = ... } reference. The child git clone inherits GIT_INDEX_FILE / GIT_DIR / GIT_WORK_TREE / GIT_OBJECT_DIRECTORY from the parent git commit env (which git sets when invoking hooks). When the parent is in a worktree, GIT_INDEX_FILE points at .git/worktrees/<name>/index, and the child git clone writes the cloned module's blob OIDs INTO that index — with OIDs that exist only in .terraform/modules/<name>/.git/objects/. The subsequent git commit then fails to resolve those OIDs in the parent worktree's object DB.
This matches the maintainer's diagnosis on pre-commit/pre-commit#1849: pre-commit framework deliberately does NOT scrub GIT_* from hook subprocess env (no_git_env() is used only internally for pre-commit's own git calls). asottile's documented punt: "hook authors that do git writes need the same code as our no_git_env helper".
pre-commit-terraform's hook scripts currently do NOT scrub GIT_*. This bug fires consistently for any user running pre-commit from a git worktree on a stack with module references.
How can we reproduce it?
Reproduction script (~30s on a fresh setup)
# Setup
mkdir /tmp/repro && cd /tmp/repro
git init && git commit --allow-empty -m init
mkdir stack
cat > stack/main.tf <<'TF'
module "test" {
source = "terraform-aws-modules/iam/aws"
version = "6.6.1"
}
TF
cat > .pre-commit-config.yaml <<'YAML'
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.106.0
hooks:
- id: terraform_validate
args:
- --hook-config=--retry-once-with-cleanup=true
- --tf-init-args=-backend=false
YAML
git add -A && git commit -m "baseline"
# Create the worktree (key step — the bug only fires from a worktree)
git worktree add ../repro-wt
cd ../repro-wt
# Install hooks + trigger
pre-commit install
echo "" >> stack/main.tf
git add stack/main.tf
git commit -m "test"
# Observe: hooks all pass, then:
# error: invalid object 100644 <oid> for '<file from cloned module repo>'
# error: Error building trees
git fsck --no-progress | head # → invalid sha1 pointer in cache-tree of worktree index
The phantom blob OIDs in the worktree's index resolve to files inside the cloned module repo (.terraform/modules/test/.git/objects/), confirming git clone's blob writes went to the wrong index.
Environment information
- OS: macOS 15.5 (Darwin arm64) — but the bug is platform-independent; we've seen it on Linux too
uname -a output:
Darwin <host> 25.5.0 Darwin Kernel Version 25.5.0: Mon Apr 27 20:41:12 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6050 arm64
- Tools availability and versions:
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin25)
pre-commit 4.6.0
OpenTofu v1.12.0
Python 3.9.6
TFLint version 0.63.1
+ ruleset.terraform (0.15.0-bundled)
trivy Version: 0.70.0
file content
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.106.0
hooks:
- id: terraform_validate
args:
- --hook-config=--retry-once-with-cleanup=true
- --tf-init-args=-backend=false
Proposed fix
Add a common::scrub_git_env helper to hooks/_common.sh that mirrors pre-commit framework's own no_git_env helper (allowlist-based: keep GIT_SSH / GIT_ASKPASS / GIT_CONFIG_* etc. so SSH-source modules still authenticate; strip everything else GIT_*). Call it from main() of every hook script that shells to git, principally terraform_validate.sh, terraform_tflint.sh, and terraform_docs.sh.
Patch sketch (bash):
# hooks/_common.sh
function common::scrub_git_env {
local var
while IFS= read -r var; do
case "$var" in
GIT_CONFIG_KEY_*|GIT_CONFIG_VALUE_*) ;;
GIT_EXEC_PATH|GIT_SSH|GIT_SSH_COMMAND|GIT_SSL_CAINFO) ;;
GIT_SSL_NO_VERIFY|GIT_CONFIG_COUNT|GIT_HTTP_PROXY_AUTHMETHOD) ;;
GIT_ALLOW_PROTOCOL|GIT_ASKPASS) ;;
GIT_*) unset "$var" ;;
esac
done < <(env | sed -n 's/^\(GIT_[^=]*\)=.*/\1/p')
}
# hooks/terraform_validate.sh (and similarly tflint, docs)
function main {
common::initialize "$SCRIPT_DIR"
common::parse_cmdline "$@"
common::export_provided_env_vars "${ENV_VARS[@]}"
common::parse_and_export_env_vars
common::scrub_git_env # ← NEW
common::per_dir_hook "$HOOK_ID" "${#ARGS[@]}" "${ARGS[@]}" "${FILES[@]}"
}
Net diff: ~15 lines _common.sh + 1 line per affected hook. No behavior change for non-worktree users (their GIT_* env is empty for these vars).
I'm happy to open a PR if the maintainer is receptive to this approach. Wanted to file the issue first for discussion since the alternative (Docker mode per pre-commit#3492) is the path the pre-commit author has suggested for this class of bug; happy to defer to the maintainer's preference between (a) in-script env scrub here, or (b) "use the Docker variant".
Related upstream issues
Describe the bug
When running
git commitfrom a linked git worktree (git worktree add ...) on a stack that references modules, the commit fails at tree-build time with:even though every pre-commit hook reports
Passed.Root cause:
terraform_validateinvokestofu init -backend=false, which shells out togit clone <module-source>for eachmodule { source = ... }reference. The childgit cloneinheritsGIT_INDEX_FILE/GIT_DIR/GIT_WORK_TREE/GIT_OBJECT_DIRECTORYfrom the parentgit commitenv (whichgitsets when invoking hooks). When the parent is in a worktree,GIT_INDEX_FILEpoints at.git/worktrees/<name>/index, and the childgit clonewrites the cloned module's blob OIDs INTO that index — with OIDs that exist only in.terraform/modules/<name>/.git/objects/. The subsequentgit committhen fails to resolve those OIDs in the parent worktree's object DB.This matches the maintainer's diagnosis on pre-commit/pre-commit#1849: pre-commit framework deliberately does NOT scrub
GIT_*from hook subprocess env (no_git_env()is used only internally for pre-commit's own git calls). asottile's documented punt: "hook authors that do git writes need the same code as ourno_git_envhelper".pre-commit-terraform's hook scripts currently do NOT scrubGIT_*. This bug fires consistently for any user running pre-commit from a git worktree on a stack with module references.How can we reproduce it?
Reproduction script (~30s on a fresh setup)
The phantom blob OIDs in the worktree's index resolve to files inside the cloned module repo (
.terraform/modules/test/.git/objects/), confirminggit clone's blob writes went to the wrong index.Environment information
uname -aoutput:.pre-commit-config.yaml:file content
Proposed fix
Add a
common::scrub_git_envhelper tohooks/_common.shthat mirrors pre-commit framework's ownno_git_envhelper (allowlist-based: keepGIT_SSH/GIT_ASKPASS/GIT_CONFIG_*etc. so SSH-source modules still authenticate; strip everything elseGIT_*). Call it frommain()of every hook script that shells to git, principallyterraform_validate.sh,terraform_tflint.sh, andterraform_docs.sh.Patch sketch (bash):
Net diff: ~15 lines
_common.sh+ 1 line per affected hook. No behavior change for non-worktree users (theirGIT_*env is empty for these vars).I'm happy to open a PR if the maintainer is receptive to this approach. Wanted to file the issue first for discussion since the alternative (Docker mode per pre-commit#3492) is the path the pre-commit author has suggested for this class of bug; happy to defer to the maintainer's preference between (a) in-script env scrub here, or (b) "use the Docker variant".
Related upstream issues
no_git_env-equivalent themselvesGIT_INDEX_FILEleakage (closed-no-fix)pre_commit/git.py:20-38— the referenceno_git_envimplementation