argoproj/argo-workflows · error

failed to put file: %w

Error message

failed to put file: %w

What it means

Wraps a failure from the Minio client PutFile call when saving a single-file S3 artifact. Argo's executor retries transient S3 errors (throttling, network blips); when the error is non-transient or retries are exhausted, this wrapper is returned so artifact saving fails the node. The %w wrapping preserves the underlying Minio/S3 error cause.

Source

Thrown at workflow/artifacts/s3/s3.go:358

			ObjectLocking: outputArtifact.S3.CreateBucketIfNotPresent.ObjectLocking,
		})
		alreadyExists := bucketAlreadyExistsErr(makeBucketErr)
		log.WithField("bucket", outputArtifact.S3.Bucket).
			WithField("alreadyExists", alreadyExists).
			WithError(makeBucketErr).
			Info(ctx, "create bucket failed")
		if makeBucketErr != nil && !alreadyExists {
			return !isTransientS3Err(ctx, makeBucketErr), fmt.Errorf("failed to create bucket %s: %w", outputArtifact.S3.Bucket, makeBucketErr)
		}
	}

	if isDir {
		if err = s3cli.PutDirectory(outputArtifact.S3.Bucket, outputArtifact.S3.Key, path); err != nil {
			return !isTransientS3Err(ctx, err), fmt.Errorf("failed to put directory: %w", err)
		}
	} else {
		if err = s3cli.PutFile(outputArtifact.S3.Bucket, outputArtifact.S3.Key, path); err != nil {
			return !isTransientS3Err(ctx, err), fmt.Errorf("failed to put file: %w", err)
		}
	}
	return true, nil
}

func bucketAlreadyExistsErr(err error) bool {
	resp := &minio.ErrorResponse{}
	// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
	alreadyExistsCodes := map[string]bool{"BucketAlreadyExists": true, "BucketAlreadyOwnedByYou": true}
	return errors.As(err, resp) && alreadyExistsCodes[resp.Code]
}

// ListObjects returns the files inside the directory represented by the Artifact
func (s3Driver *ArtifactDriver) ListObjects(ctx context.Context, artifact *wfv1.Artifact) ([]string, error) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	var files []string

View on GitHub (pinned to 35bff19146)

Solutions

  1. Check the wrapped underlying error cause for the real S3 failure (permissions, missing bucket, credentials)
  2. Verify s3 bucket/endpoint/keyFormat in the artifact repository config and the artifact's S3 key
  3. Confirm the artifact source path exists in the container and the step actually produced the file
  4. Test credentials with aws s3 cp or mc cp against the same endpoint/bucket

Example fix

// before
s3cli.PutFile(bucket, key, path) // fails: bucket missing
// after
# create/verify bucket first
aws --endpoint-url $ENDPOINT s3 mb s3://my-bucket
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: ensure artifact source exists and bucket is writable
if _, err := os.Stat(path); err != nil { return fmt.Errorf("artifact path missing: %w", err) }
_, err := mc.StatObject(ctx, bucket, keyPrefix, minio.StatObjectOptions{})

Try / catch

// executor already retries transient errors; on workflow failure
if strings.Contains(err.Error(), "failed to put file") {
  log.Printf("S3 put failed, check bucket/creds: %v", errors.Unwrap(err))
}

Prevention

When it happens

Trigger: saveS3Artifact calls s3cli.PutFile and Minio returns an error: wrong bucket/key permissions, missing bucket, nonexistent local path, invalid credentials, or S3 outage beyond the retry backoff.

Common situations: IAM/Minio policy denies s3:PutObject; artifact repository bucket misconfigured in workflow-controller-configmap; accessKey/secretKey wrong or rotated; local file deleted before save; endpoint TLS misconfiguration.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/273ba561726e8eaa. Report an issue: GitHub.