thephpleague/flysystem · error · UnableToProvideChecksum

No checksum provided in metadata

Error message

No checksum provided in metadata

What it means

AzureBlobStorageAdapter::checksum() found the blob and read its properties, but the md5_checksum extra field is absent, meaning Content-MD5 was not stored on the blob. Azure only populates Content-MD5 when it was set at upload time (or the client computed it), so many blobs written without that header have no retrievable checksum, and Flysystem refuses to invent one.

Source

Thrown at src/AzureBlobStorage/AzureBlobStorageAdapter.php:366

    }

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

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

        try {
            $metadata = $this->fetchMetadata($this->prefixer->prefixPath($path));
            $checksum = $metadata->extraMetadata()['md5_checksum'] ?? '__not_specified';
        } catch (Throwable $exception) {
            throw new UnableToProvideChecksum($exception->getMessage(), $path, $exception);
        }

        if ($checksum === '__not_specified') {
            throw new UnableToProvideChecksum('No checksum provided in metadata', $path);
        }

        return bin2hex(base64_decode($checksum));
    }

    public function temporaryUrl(string $path, DateTimeInterface $expiresAt, Config $config): string
    {
        if ( ! $this->serviceSettings instanceof StorageServiceSettings) {
            throw UnableToGenerateTemporaryUrl::noGeneratorConfigured(
                $path,
                'The $serviceSettings constructor parameter must be set to generate temporary URLs.',
            );
        }

        try {
            $sas = new BlobSharedAccessSignatureHelper($this->serviceSettings->getName(), $this->serviceSettings->getKey());
            $baseUrl = $this->publicUrl($path, $config);
            $resourceName = $this->container . '/' . ltrim($this->prefixer->prefixPath($path), '/');

View on GitHub (pinned to b277b5dc3d)

Solutions

  1. Re-upload (or re-write via Put Blob/Copy) the object with the Content-MD5 header set to the base64 md5 of the content.
  2. Or compute the digest client-side: hash $filesystem->readStream($path) with hash_init('md5')/hash_update_stream — Filesystem::checksum() with 'checksum_algo' left at its md5 default only helps if the adapter is bypassed.
  3. Audit writers so every upload sets Content-MD5 (Azure SDK setMetadata/contentMd6 option or 'Content-MD5' header on createBlockBlob).
  4. Catch UnableToProvideChecksum and treat 'No checksum provided in metadata' as a data-quality signal to repair the blob.

Example fix

// before
$checksum = $azureAdapter->checksum('legacy/file.dat', new Config()); // blob has no Content-MD5 -> throws

// after (compute from stream when stored MD5 is absent)
try {
    $checksum = $azureAdapter->checksum('legacy/file.dat', new Config());
} catch (UnableToProvideChecksum $e) {
    $stream = $filesystem->readStream('legacy/file.dat');
    $ctx = hash_init('md5');
    hash_update_stream($ctx, $stream);
    $checksum = hash_final($ctx);
    // optionally repair: re-upload with 'Content-MD5' => base64_encode(hex2bin($checksum))
}
Defensive patterns

Strategy: fallback

Validate before calling

// There is no reliable pre-check for stored Content-MD5 other than fetching metadata:
try {
    $md5 = $adapter->checksum($path, new Config());
} catch (UnableToProvideChecksum $e) {
    // handled below
}
// For new uploads, set Content-MD5 at write time so this path never triggers:
$filesystem->write($path, $contents, ['headers' => ['Content-MD5' => base64_encode(pack('H*', md5($contents)))]]);

Try / catch

use League\Flysystem\UnableToProvideChecksum;

try {
    $md5 = $filesystem->checksum($path);
} catch (UnableToProvideChecksum $e) {
    if (str_contains($e->getMessage(), 'No checksum provided in metadata')) {
        // blob has no stored Content-MD5: compute it and repair the blob
        $stream = $filesystem->readStream($path);
        $ctx = hash_init('md5');
        hash_update_stream($ctx, $stream);
        $md5 = hash_final($ctx);
        $filesystem->write($path, stream_get_contents($stream), [
            'headers' => ['Content-MD5' => base64_encode(hex2bin($md5))],
        ]);
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling checksum() on a blob uploaded without a Content-MD5 header — typical when the blob was written by third-party tools, azcopy defaults, Put Blob calls from other SDKs, or Flysystem's own write() (which does not set Content-MD5). Also blobs whose MD5 was never populated server-side.

Common situations: Migrating pre-existing data into checksum-verified pipelines; mixed writers (some set Content-MD5, some don't); assuming Azure computes MD5 automatically like S3 does ETag.

Related errors


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