Skip to content

resolveCheckoutRef silently discards underlying git errors, replacing them with a misleading "invalid ref" message #649

Description

@eehret

Description

When Terraform fails to download a registry module, it can report invalid ref: "<sha>" — which looks like a URL or ref formatting problem — when the real cause is something completely different (e.g., a safe.directory ownership check, a network failure, or a permission error). The actual git error message is captured and thrown away.

Root cause

resolveCheckoutRef in get_git.go uses cmd.Output() to run git rev-parse, which captures both stdout and stderr. When the command fails, the error output from git is silently discarded and a generic message is returned instead:

for _, candidate := range candidates {
    cmd := exec.CommandContext(ctx, "git", "rev-parse", ...)
    cmd.Dir = dst

    resolvedRef, err := cmd.Output() // stderr captured here and never surfaced
    if err == nil {
        return strings.TrimSpace(string(resolvedRef)), nil
    }
    // actual git error is dropped
}

return "", fmt.Errorf("invalid ref: %q", ref) // real cause is lost

Reproduction

  1. Use Terraform in a dev container (or any environment) where the workspace is mounted from a host with a different user ID — a common setup on macOS/Windows.
  2. Run terraform init.
  3. Observe: invalid ref: "<sha>" is reported for every module.
  4. The actual git error (fatal: detected dubious ownership in repository at '...') is never shown.

The misleading error sends users to investigate the git ref or registry URL rather than the actual problem.

Expected behaviour

The underlying git error(s) should be included in the returned error so the caller (and ultimately the user) can see what actually went wrong. For example:

var errMsgs []string
for _, candidate := range candidates {
    cmd := exec.CommandContext(ctx, "git", "rev-parse", ...)
    cmd.Dir = dst

    resolvedRef, err := cmd.Output()
    if err == nil {
        return strings.TrimSpace(string(resolvedRef)), nil
    }
    if exitErr, ok := err.(*exec.ExitError); ok {
        errMsgs = append(errMsgs, strings.TrimSpace(string(exitErr.Stderr)))
    }
}

return "", fmt.Errorf("invalid ref %q: %s", ref, strings.Join(errMsgs, "; "))

Environment

  • go-getter v1.8.6 (embedded in Terraform v1.15.7)
  • git 2.51.0
  • Linux arm64 (dev container with macOS host volume mount)

Additional context

Prior to the introduction of resolveCheckoutRef, git errors were more likely to propagate to the user. The new multi-candidate resolution approach is a useful feature, but the current error handling makes failures very difficult to diagnose.

NOTE: The details in this issue were composed by Copilot after it diagnosed the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions