Skip to content

Provider archives downloaded by terraform init are left in the temporary directory on Windows #39434

Open

Description

@rmagri

Terraform Version

Terraform v1.11.3
on windows_amd64

Also reproduced with a local build of main at cdbff3e.

Terraform Configuration Files

terraform {
  required_providers {
    null = {
      source  = "hashicorp/null"
      version = "3.2.1"
    }
  }
}

Debug Output

Not applicable: nothing is logged, because the error from the failed delete is discarded.

Expected Behavior

After terraform init installs a provider downloaded from a registry, the downloaded archive is deleted from the temporary directory.

Actual Behavior

The archive stays behind as %TEMP%\terraform-provider<random>: one file for every provider package downloaded, on every init (4.7 MB for hashicorp/null 3.2.1, about 180 MB for hashicorp/aws). terraform providers lock leaves one per provider per requested platform. On one workstation 1,764 of these files (195 GB) built up in ten days and filled the system drive.

Steps to Reproduce

PowerShell, in an empty directory:

  1. Save the configuration above as work\main.tf.
  2. mkdir tmp; $env:TMP = "$PWD\tmp"; $env:TEMP = "$PWD\tmp"
  3. terraform -chdir=work init
  4. Get-ChildItem tmp lists terraform-provider<N>.

Additional Context

installFromHTTPURL in internal/providercache/package_install.go:

f, err := os.CreateTemp("", "terraform-provider")
// ...
defer f.Close()
defer os.Remove(f.Name())

Deferred calls run last-in, first-out, so os.Remove runs while f is still open. On Windows that removal fails and its error is discarded. Linux and macOS allow removing a file that is still open, so the leak only shows on Windows.

The handle is only used to reserve the name (HttpGetter.GetFile opens the file again by path), so closing it straight away and deferring only the removal fixes it. That change, with a regression test, is on a branch: rmagri@a74cbfb. With a local build of main the reproduction above leaves the archive behind; with the change it leaves nothing. I'm happy to open a PR if the approach is acceptable.

Workaround until then: with a dependency lock file and TF_PLUGIN_CACHE_DIR set, installs served from the cache download nothing and leave nothing behind.

References

None found.

Activity

  1. crw commented on Oct 22, 2026

    @crw
    Contributor

    Thanks for the report!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions