argoproj/argo-workflows · error

failed to test if %s/%s is a directory: %w

Error message

failed to test if %s/%s is a directory: %w

What it means

After OSS returns NoSuchKey, Load checks whether the key is actually an OSS 'directory' via IsOssDirectory (a list-objects call under the prefix). If that directory probe itself errors (permissions on ListObjects, network failure), Load fails with 'failed to test if <bucket>/<key> is a directory'.

Source

Thrown at workflow/artifacts/oss/oss.go:161

				return !isTransientOSSErr(ctx, err), err
			}
			objectName := inputArtifact.OSS.Key
			dirPath := filepath.Dir(path)
			err = os.MkdirAll(dirPath, 0o700)
			if err != nil {
				return false, fmt.Errorf("mkdir %s error: %w", dirPath, err)
			}
			origErr := bucket.GetObjectToFile(objectName, path)
			if origErr == nil {
				return true, nil
			}
			if !IsOssErrCode(origErr, "NoSuchKey") {
				return !isTransientOSSErr(ctx, origErr), fmt.Errorf("failed to get file: %w", origErr)
			}
			// If we get here, the error was a NoSuchKey. The key might be a oss "directory"
			isDir, err := IsOssDirectory(bucket, objectName)
			if err != nil {
				return !isTransientOSSErr(ctx, err), fmt.Errorf("failed to test if %s/%s is a directory: %w", bucketName, objectName, err)
			}
			if !isDir {
				// It's neither a file, nor a directory. Return the original NoSuchKey error
				return false, origErr
			}

			if err = GetOssDirectory(ctx, bucket, objectName, path); err != nil {
				return !isTransientOSSErr(ctx, err), fmt.Errorf("failed get directory: %w", err)
			}
			return true, nil
		})
	return err
}

// OpenStream opens a stream reader for an artifact from OSS compliant storage
func (ossDriver *ArtifactDriver) OpenStream(ctx context.Context, inputArtifact *wfv1.Artifact) (io.ReadCloser, error) {
	var stream io.ReadCloser
	err := waitutil.Backoff(defaultRetry,

View on GitHub (pinned to 35bff19146)

Solutions

  1. Grant the OSS credentials oss:ListObjects (ListBucket) permission on the bucket.
  2. If the artifact should be a file, check the exact key spelling — a wrong key triggers the NoSuchKey+probe path; fix the key to avoid the probe entirely.
  3. For intermittent causes, retry the workflow; isTransientOSSErr already filters retryable codes.
  4. Ensure the STS role policy includes both GetObject and ListBucket actions.

Example fix

// before (RAM policy)
{ "Statement": [{ "Action": ["oss:GetObject"], "Effect": "Allow" }] }
// after
{ "Statement": [{ "Action": ["oss:GetObject", "oss:ListObjects"], "Effect": "Allow" }] }
Defensive patterns

Strategy: validation

Validate before calling

// ensure creds can list before workflows that may hit directory keys
cli, _ := oss.New(endpoint, ak, sk)
b, _ := cli.Bucket(bucket)
_, err := b.ListObjects(oss.Prefix(key), oss.MaxKeys(1))
if err != nil {
	return fmt.Errorf("credentials lack ListObjects on %s: %w", bucket, err)
}

Try / catch

err := driver.Load(ctx, art, path)
if err != nil && strings.Contains(err.Error(), "is a directory") {
	// list-permission or transient failure during NoSuchKey probe; check RAM policy
}

Prevention

When it happens

Trigger: GetObjectToFile returns NoSuchKey and the follow-up IsOssDirectory(bucket, objectName) errors — most often the credentials lack oss:ListObjects permission on the bucket, or a transient network failure during the list call after retries.

Common situations: RAM policies that grant GetObject but not ListObjects (common with least-privilege setups); keys pointing to folder-style prefixes while the policy only allows reading specific objects; intermittent OSS connectivity with retry budget exhausted.

Related errors


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