kubernetes/kops · error

write to %s with ACL of unexpected type %T

Error message

write to %s with ACL of unexpected type %T

What it means

GSPath.WriteFile expects the ACL argument to be a *GSAcl, the Google-Storage-specific ACL implementation in this package. If a caller passes an ACL built for a different VFS backend (e.g. *S3Acl, *SSHAcl, *VFSAcl), it fails immediately with this message inside the retry closure. This is a programming error: ACL types are backend-specific and must match the path type.

Source

Thrown at util/pkg/vfs/gsfs.go:188

	return &GSPath{
		vfsContext: p.vfsContext,
		bucket:     p.bucket,
		key:        joined,
	}
}

func (p *GSPath) WriteFile(ctx context.Context, data io.ReadSeeker, acl ACL) error {
	md5Hash, err := hashing.HashAlgorithmMD5.Hash(data)
	if err != nil {
		return err
	}

	done, err := RetryWithBackoff(gcsWriteBackoff, func() (bool, error) {
		var objectACL []storage.ACLRule
		if acl != nil {
			gsACL, ok := acl.(*GSAcl)
			if !ok {
				return true, fmt.Errorf("write to %s with ACL of unexpected type %T", p, acl)
			}
			objectACL = gsACL.Acl
			klog.V(4).Infof("Writing file %q with ACL %v", p, gsACL)
		} else {
			klog.V(4).Infof("Writing file %q", p)
		}

		if _, err := data.Seek(0, 0); err != nil {
			return false, fmt.Errorf("error seeking to start of data stream for write to %s: %v", p, err)
		}

		client, err := p.getStorageClient(ctx)
		if err != nil {
			return false, err
		}

		w := client.Bucket(p.bucket).Object(p.key).NewWriter(ctx)
		// The upload is rejected if the data does not match this MD5 hash

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Construct the ACL for the GCS backend: use vfs.NewACL("gs") (or &vfs.GSAcl{Acl: []storage.ACLRule{...}}) instead of the S3/other-backend ACL type.
  2. If the code supports multiple backends, select the ACL based on the path type: switch p.(type) { case *vfs.GSPath: acl = vfs.NewACL("gs"); case *vfs.S3Path: acl = vfs.NewACL("s3") }.
  3. If no special ACL is required, pass nil — WriteFile then writes with the bucket's default object ACL and skips the type check entirely.

Example fix

// before
acl := vfs.NewACL("s3")            // S3Acl
gsPath.WriteFile(ctx, data, acl)   // panics into "ACL of unexpected type"
// after
acl := vfs.NewACL("gs")            // *GSAcl, matches gs:// path
gsPath.WriteFile(ctx, data, acl)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the ACL type before calling WriteFile:
func validateACL(p vfs.Path, acl vfs.ACL) error {
    if acl == nil {
        return nil // nil is always accepted
    }
    if _, ok := p.(*vfs.GSPath); ok {
        if _, ok := acl.(*vfs.GSAcl); !ok {
            return fmt.Errorf("gs:// path requires *vfs.GSAcl, got %T", acl)
        }
    }
    return nil
}

Type guard

func isGSAcl(acl vfs.ACL) bool {
    _, ok := acl.(*vfs.GSAcl)
    return ok
}

Try / catch

if err := gsPath.WriteFile(ctx, data, acl); err != nil {
    if strings.Contains(err.Error(), "ACL of unexpected type") {
        // programmer error: rebuild ACL for the GCS backend and retry once
        if werr := gsPath.WriteFile(ctx, data, vfs.NewACL("gs")); werr != nil {
            return werr
        }
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling GSPath.WriteFile(ctx, data, acl) with a non-nil acl that is not *GSAcl — typically constructed via vfs.NewACL("s3") (or another provider) while writing to a gs:// path, or passing an ACL object obtained from an S3/FS path into GCS write code.

Common situations: Code that is cloud-agnostic but hardcodes one ACL (common when porting an AWS tool to GCP); a config option like --acl fed straight into WriteFile without mapping to the backend; tests or shared helpers reusing a single ACL value across backends.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/3bd16b4c18ff4920. Report an issue: GitHub.