thephpleague/flysystem · error · UnableToProvideChecksum

Unable to get checksum for $path: $reason

Error message

Unable to get checksum for $path: $reason

What it means

AwsS3V3Adapter::checksum() performs a HeadObject via fetchFileMetadata(); if that raises UnableToRetrieveMetadata (file missing, access denied, transport error), it is wrapped as UnableToProvideChecksum with message 'Unable to get checksum for $path: $reason' and the original exception chained. This is the generic failure path for every non-ETag-specific checksum problem on the AWS SDK v3 adapter.

Source

Thrown at src/AwsS3V3/AwsS3V3Adapter.php:499

        try {
            return $this->client->getObjectUrl($this->bucket, $location);
        } catch (Throwable $exception) {
            throw UnableToGeneratePublicUrl::dueToError($path, $exception);
        }
    }

    public function checksum(string $path, Config $config): string
    {
        $algo = $config->get('checksum_algo', 'etag');

        if ($algo !== 'etag') {
            throw new ChecksumAlgoIsNotSupported();
        }

        try {
            $metadata = $this->fetchFileMetadata($path, 'checksum')->extraMetadata();
        } catch (UnableToRetrieveMetadata $exception) {
            throw new UnableToProvideChecksum($exception->reason(), $path, $exception);
        }

        if ( ! isset($metadata['ETag'])) {
            throw new UnableToProvideChecksum('ETag header not available.', $path);
        }

        return trim($metadata['ETag'], '"');
    }

    public function temporaryUrl(string $path, DateTimeInterface $expiresAt, Config $config): string
    {
        try {
            $options = $config->get('get_object_options', []);
            $command = $this->client->getCommand('GetObject', [
                    'Bucket' => $this->bucket,
                    'Key' => $this->prefixer->prefixPath($path),
                ] + $options);

View on GitHub (pinned to b277b5dc3d)

Solutions

  1. Check existence with fileExists() before checksumming and treat a negative result as a missing-file error.
  2. Read $e->getPrevious()->getMessage() to identify the HeadObject error code (404, 403, 400 wrong region) and fix the underlying client config or policy.
  3. For 403s, add s3:GetObject on the object ARN plus s3:ListBucket on the bucket so S3 returns 404 instead of 403 for missing keys.
  4. Wrap in try/catch (UnableToProvideChecksum) with a retry policy for transient network faults.

Example fix

// before
$etag = $filesystem->checksum('uploads/' . $id . '.zip'); // wrong id -> 404 -> throws

// after
try {
    $etag = $filesystem->checksum('uploads/' . $id . '.zip');
} catch (UnableToProvideChecksum $e) {
    $previous = $e->getPrevious();
    if ($previous instanceof UnableToRetrieveMetadata && str_contains($previous->message(), '404')) {
        return new NotFoundResponse($id);
    }
    throw $e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ( ! $filesystem->fileExists($path)) {
    // treat as missing artifact before attempting a HEAD-based checksum
    return $fallback ?: throw new DomainException("Artifact not found: {$path}");
}

Try / catch

use League\Flysystem\UnableToProvideChecksum;

try {
    $etag = $filesystem->checksum($path);
} catch (UnableToProvideChecksum $e) {
    $previous = $e->getPrevious();               // UnableToRetrieveMetadata
    $reason = $previous?->getMessage() ?? $e->getMessage();
    if (str_contains($reason, '404') || str_contains($reason, 'NotFound')) {
        return $onMissing($path);
    }
    if (str_contains($reason, '403') || str_contains($reason, 'AccessDenied')) {
        return $onForbidden($path);
    }
    throw $e; // transport/unknown: retryable at a higher level
}

Prevention

When it happens

Trigger: $filesystem->checksum() / $adapter->checksum() on a nonexistent key, a key that is a directory marker, when the IAM policy denies s3:GetObject, when the SDK client has stale credentials or a wrong region, or when a filesystem path prefix maps to a missing S3 prefix.

Common situations: Key casing or prefix mistakes; environments with scoped-down IAM roles; cross-region misconfigurations producing 301/400 HeadObject errors; checksum verification after a concurrent delete or overwrite.

Related errors


AI-assisted analysis of thephpleague/flysystem@b277b5dc3d (2026-08-17). Data as JSON: /api/errors/a250969a7a5ce04e. Report an issue: GitHub.