juicedata/juicefs · error

deleting a non-existent object returns an error %v

Error message

deleting a non-existent object returns an error %v

What it means

This error is raised by the dedicated 'delete non-exist' objbench case when a backend returns an error for deleting an object that was never created. JuiceFS expects every ObjectStorage implementation to make Delete idempotent — deleting a nonexistent key must return nil. The message wraps the backend's error (typically a 404 or ENOENT).

Source

Thrown at cmd/objbench.go:878

		if err := blob.Put(ctx, key, bytes.NewReader(br)); err != nil {
			return fmt.Errorf("put object failed: %s", err)
		}
		if err := blob.Delete(ctx, key); err != nil {
			return fmt.Errorf("delete failed: %s", err)
		}
		if _, err := blob.Head(ctx, key); err == nil {
			return fmt.Errorf("expect err is not nil")
		}

		if err := blob.Delete(ctx, key); err != nil {
			return fmt.Errorf("delete not existed: %v", err)
		}
		return nil
	})

	runCase("delete non-exist", func(blob object.ObjectStorage) error {
		if err := blob.Delete(ctx, key); err != nil {
			return fmt.Errorf("deleting a non-existent object returns an error %v", err)
		}
		return nil
	})

	runCase("list objects", func(blob object.ObjectStorage) error {
		br := []byte("hello")
		if err := blob.Put(ctx, key, bytes.NewReader(br)); err != nil {
			return fmt.Errorf("put object failed: %s", err)
		}
		defer blob.Delete(ctx, key) //nolint:errcheck
		if isFileSystem {
			objs, err := listAll(ctx, blob, "", "", 2)
			if err == nil {
				if len(objs) != 2 {
					return fmt.Errorf("list should return 2 keys, but got %d", len(objs))
				}
				if objs[0].Key() != "" {
					return fmt.Errorf("first key should be empty string, but got %s", objs[0].Key())

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Make the backend's Delete return nil on not-found (map 404/NoSuchKey/ENOENT to success), matching S3 and POSIX semantics.
  2. Check the wrapped error text to identify which backend call returned it.
  3. If testing a custom store, add explicit handling for the vendor's not-found error code.
  4. Add a unit/regression test asserting Delete of a missing key returns nil.
  5. Rerun `juicefs objbench <storage-url>` to confirm.

Example fix

// before
return os.Remove(path) // ENOENT surfaces to caller
// after
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
    return err
}
return nil
Defensive patterns

Strategy: validation

Validate before calling

// pre-check existence if the backend lacks idempotent delete
if _, err := blob.Head(ctx, key); err == nil {
    if err := blob.Delete(ctx, key); err != nil { return err }
}

Try / catch

err := blob.Delete(ctx, key)
if err != nil && isNotFound(err) {
    return nil // acceptable: object already absent
}

Prevention

When it happens

Trigger: Directly calling blob.Delete(ctx, key) for a key that does not exist in the bucket, and the backend returns a not-found error instead of treating the delete as a no-op.

Common situations: Custom or vendor-specific ObjectStorage implementations that surface 404s; testing against S3-compatible services (e.g. some MinIO configurations, Ceph gateways, or private cloud stores) that return NoSuchKey on delete; filesystem-backed stores leaking os.Remove's ENOENT.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/09ea508e92990e04. Report an issue: GitHub.