hashicorp/terraform · error

failed to upload state: %w

Error message

failed to upload state: %w

What it means

Thrown in RemoteClient.put() (s3/client.go:240) when the S3 manager Uploader fails to PutObject the state file during a write. The wrapped (%w) error is the raw AWS SDK error. This aborts the state write; because the MD5 digest is written afterward, a failed upload means no digest row is created either.

Source

Thrown at internal/backend/remote-state/s3/client.go:240

			input.SSECustomerAlgorithm = aws.String(string(s3EncryptionAlgorithm))
			input.SSECustomerKeyMD5 = aws.String(c.getSSECustomerKeyMD5())
		} else {
			input.ServerSideEncryption = s3EncryptionAlgorithm
		}
	}

	if c.acl != "" {
		input.ACL = s3types.ObjectCannedACL(c.acl)
	}

	log.Info("Uploading remote state")

	uploader := manager.NewUploader(c.s3Client, func(u *manager.Uploader) {
		u.ClientOptions = optFns
	})
	_, err := uploader.Upload(ctx, input)
	if err != nil {
		return fmt.Errorf("failed to upload state: %w", err)
	}

	if err := c.putMD5(ctx, sum[:]); err != nil {
		// if this errors out, we unfortunately have to error out altogether,
		// since the next Get will inevitably fail.
		return fmt.Errorf("failed to store state MD5: %w", err)
	}

	return nil
}

func (c *RemoteClient) Delete() tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics
	ctx := context.TODO()
	log := c.logger(operationClientDelete)

	ctx, baselog := baselogging.NewHcLogger(ctx, log)
	ctx = baselogging.RegisterLogger(ctx, baselog)

View on GitHub (pinned to c9def3e214)

Solutions

  1. Grant s3:PutObject on the state key (and kms:Encrypt/GenerateDataKey for SSE-KMS).
  2. Verify the kms_key_id exists, is enabled, and is in the same region as the bucket.
  3. Check for Object Lock / bucket policy denying the overwrite.
  4. Retry on SlowDown/throttling with backoff.

Example fix

// before: role can read but not write state

// after
// {
//   "Effect": "Allow",
//   "Action": ["s3:PutObject"],
//   "Resource": "arn:aws:s3:::mycorp-tfstate/*"
// }
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe write (and KMS) access before the apply writes state
// _, err := s3Client.PutObject(ctx, &s3.PutObjectInput{Bucket:&bucket, Key:aws.String("tf-probe"), Body: bytes.NewReader([]byte("{}"))})
// if err != nil { /* grant s3:PutObject / kms:Encrypt */ }

Type guard

// React by AWS error code on the wrapped upload error
// var apiErr smithy.APIError
// if errors.As(err, &apiErr) {
//   switch apiErr.ErrorCode() {
//   case "AccessDenied": /* s3:PutObject / kms:Encrypt */
//   case "SlowDown":     /* retry */
//   }
// }

Try / catch

// Retry throttling, surface permission errors
// var apiErr smithy.APIError
// if errors.As(err, &apiErr) && apiErr.ErrorCode()=="SlowDown" { /* backoff retry */ } else { return err }

Prevention

When it happens

Trigger: s3:PutObject denied; SSE-KMS key id invalid/unreachable or kms:Encrypt missing; bucket policy or Object Lock (WORM) blocking the write; S3 throttling; bucket in a different region than the client; wrong ACL string.

Common situations: Least-privilege role missing s3:PutObject; KMS key id typo or key disabled; Object Lock retention preventing overwrite; cross-account bucket without proper bucket-policy + KMS grants; ACL set to an unsupported value.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/97df555c594cbdb6. Report an issue: GitHub.