thephpleague/flysystem · error · UnableToProvideChecksum

ETag header not available.

Error message

ETag header not available.

What it means

The HeadObject call in AsyncAwsS3Adapter::checksum() succeeded, but the returned extraMetadata array contains no 'ETag' key, so no checksum can be produced. Real AWS S3 always returns an ETag on HEAD, so hitting this usually means the response came from an S3-compatible service or an intermediary that omits it.

Source

Thrown at src/AsyncAwsS3/AsyncAwsS3Adapter.php:569

        }
    }

    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 {
            $request = new GetObjectRequest([
                'Bucket' => $this->bucket,
                'Key' => $this->prefixer->prefixPath($path),
            ] + $config->get('get_object_options', []));

            return $this->client->presign($request, DateTimeImmutable::createFromInterface($expiresAt));
        } catch (Throwable $exception) {
            throw UnableToGenerateTemporaryUrl::dueToError($path, $exception);
        }
    }

View on GitHub (pinned to b277b5dc3d)

Solutions

  1. Reproduce with a direct HeadObject (aws s3api head-object or the SDK) to confirm the server actually omits ETag.
  2. If the server is at fault, upgrade/reconfigure it, or switch the checksum path to stream hashing via Filesystem::checksum() with an algorithm the adapter does not support (falls back to hashing the read stream).
  3. In tests, build the mocked HeadObjectResponse with a realistic ETag (e.g. '"d41d8cd98f00b204e9800998ecf8427e"').
  4. Catch UnableToProvideChecksum and check the message for 'ETag header not available.' to distinguish it from network/auth causes.

Example fix

// before (mock without ETag)
$client->headObject(['Bucket' => 'b', 'Key' => 'k'])->resolve(); // result has no ETag
$adapter->checksum('k', new Config()); // throws 'ETag header not available.'

// after (realistic mock)
$result = new HeadObjectResponse();
$result->initialize(['ETag' => '"9b2cf535f27731c974343645a3985328"']);
Defensive patterns

Strategy: fallback

Validate before calling

// Smoke-test the endpoint once at boot: HEAD an object and confirm ETag presence
$response = $s3Client->headObject(['Bucket' => $bucket, 'Key' => $probeKey])->resolve();
if ($response->getETag() === null) {
    // endpoint omits ETag: route checksums to the stream-hashing path from now on
    $useStreamChecksums = true;
}

Try / catch

try {
    $checksum = $filesystem->checksum($path, ['checksum_algo' => 'etag']);
} catch (UnableToProvideChecksum $e) {
    if (str_contains($e->getMessage(), 'ETag header not available.')) {
        $ctx = hash_init('md5');
        hash_update_stream($ctx, $filesystem->readStream($path));
        $checksum = hash_final($ctx);
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Running checksum() against an S3-compatible server (MinIO, Ceph RGW, storage gateways) that does not include ETag in HEAD responses; using a mocked/stubbed AsyncAws result object in tests that was built without setting the ETag header; a proxy or signed-URL layer stripping object metadata.

Common situations: Local/dev environments use MinIO while production uses AWS and behavior differs; test suites with hand-constructed HeadObjectResponse mocks; exotic object stores fronted by custom HTTP middleware.

Related errors


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