kopia/kopia · error

unexpected error

Error message

unexpected error

What it means

ValidateProvider's capacity check wraps any unexpected GetCapacity error with the generic message 'unexpected error'. It is used when the storage implementation returns an error other than the tolerated blob.ErrNotAVolume while reporting volume capacity.

Solutions

  1. Inspect the wrapped cause — it contains the actual GetCapacity failure from the provider.
  2. Verify credentials have permission for account/capacity metadata APIs.
  3. Retry after transient network issues resolve.
  4. If the backend genuinely lacks capacity support, make its GetCapacity return blob.ErrNotAVolume instead of a generic error.

Example fix

// before
return blob.Capacity{}, errors.New("not implemented")
// after
return blob.Capacity{}, blob.ErrNotAVolume
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := st.GetCapacity(ctx); err != nil && !errors.Is(err, blob.ErrNotAVolume) {
    return fmt.Errorf("capacity API unavailable: %w", err)
}

Type guard

func isNotAVolume(err error) bool {
    return errors.Is(err, blob.ErrNotAVolume)
}

Try / catch

if err := providervalidation.ValidateProvider(ctx, st, opts); err != nil {
    var capErr = strings.Contains(err.Error(), "unexpected error")
    if capErr && isAuthErr(errors.Unwrap(err)) {
        refreshCredentials(); return retry()
    }
    return err
}

Prevention

When it happens

Trigger: st.pickOne().GetCapacity(ctx) fails with a real error (auth failure, network timeout, throttling, API error) rather than returning a Capacity or blob.ErrNotAVolume.

Common situations: Expired storage credentials hitting the provider's capacity/statistics API; Azure requests rejected due to missing permission to query account metadata; transient network failure during validation.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/f524c916b5c5432e. Report an issue: GitHub.

Appendix: source

Thrown at internal/providervalidation/providervalidation.go:137

	}()

	uberPrefix := blob.ID("z" + uuid.NewString())
	defer cleanupAllBlobs(ctx, st[0], uberPrefix)

	prefix1 := uberPrefix + "a"
	prefix2 := uberPrefix + "b"

	log(ctx).Info("Validating storage capacity and usage")

	c, err := st.pickOne().GetCapacity(ctx)

	switch {
	case errors.Is(err, blob.ErrNotAVolume):
		// This is okay. We expect some implementations to not support this method.
	case c.FreeB > c.SizeB:
		return errors.Errorf("expected volume's free space (%dB) to be at most volume size (%dB)", c.FreeB, c.SizeB)
	case err != nil:
		return errors.Wrapf(err, "unexpected error")
	}

	log(ctx).Info("Validating blob list responses")

	if err := verifyBlobCount(ctx, st.pickOne(), uberPrefix, 0); err != nil {
		return errors.Wrap(err, "invalid blob count")
	}

	log(ctx).Info("Validating non-existent blob responses")

	var out gather.WriteBuffer
	defer out.Close()

	// read non-existent full blob
	if err := st.pickOne().GetBlob(ctx, prefix1+"1", 0, -1, &out); !errors.Is(err, blob.ErrBlobNotFound) {
		return errors.Errorf("got unexpected error when reading non-existent blob: %v", err)
	}

View on GitHub (pinned to 82495e54b5)