Currently i am using this module in a site with locale module active, and i getting this warning:

User warning: Stream is not seekable in Aws\S3\StreamWrapper->triggerError() (line 788 of /var/www/html/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php).

Error message
Warning: file_get_contents(public://languages/es_VDvIBCKufkGMU2tQ1_KFvYXnRgrAosuv8CW9BTOTRJQ.js): Failed to open stream: "Drupal\s3fs\StreamWrapper\PublicS3fsStream::stream_open" call failed in Drupal\system\Controller\JsAssetController->generateHash() (line 43 of core/lib/Drupal/Core/Asset/AssetGroupSetHashTrait.php).

My current configuration:

$config['s3fs.settings'] = [
    'bucket' => $bucket_name,
    'region' => $bucket_region,
    'use_customhost' => FALSE,
    'use_https' => TRUE,
    'use_s3_for_public' => TRUE,
    'use_s3_for_private' => TRUE,
    'presigned_urls' => TRUE,
    'use_cname' => FALSE,
    'no_rewrite_cssjs' => FALSE,
    'torrents' => FALSE,

  ];

  $settings['s3fs.use_s3_for_public'] = TRUE;
  $settings['s3fs.use_s3_for_private'] = TRUE;
  $settings['php_storage']['twig']['directory'] = "/tmp/php";
  $config['system.file']['default_scheme'] = "s3";
  $config['system.performance']['js']['preprocess'] = FALSE; // This is the hack

As you can see the only way to stop this warning is to disable the JS agregation.

Also Drupal warns me about the the .htaccess file doesn't exists in public://and private:// that's cause internally drupal use @file_get_contents() to verify file contents.

Note that all files exists in my bucket(languages/es_VDvIBCKufkGMU2tQ1_KFvYXnRgrAosuv8CW9BTOTRJQ.js, public://.htaccess and private://.htaccess).

I think we must implement a temporary download to local file for ready only operations that requires seek()

Can you guide to decide how to manage this so i can try to help to make some MR to fix this?.

Comments

metallized created an issue. See original summary.

metallized’s picture

Title: Locale module support » Stream wrapper fails on file_get_contents() due to non-seekable stream
Issue summary: View changes
metallized’s picture

Issue summary: View changes
metallized’s picture

Issue summary: View changes
cmlara’s picture

I think we must implement a temporary download to local file for ready only operations that requires seek()

I believe that should already be done by https://git.drupalcode.org/project/s3fs/-/blob/88e5265fa00d515bdf07b0bcc... (NOTE: the AWS SDK should only ever see s3:// based paths, public:// and private:// are aliased to their relevant path. This comes from #2496033: Using V3 of the AWS SDK.

Do you have a s3fs_stream_open_params_alter hook that is removing the seekable option?

As you can see the only way to stop this warning is to disable the JS agregation.

Warning: file_get_contents(public://languages/es_VDvIBCKufkGMU2tQ1_KFvYXnRgrAosuv8CW9BTOTRJQ.js): Failed to open stream: "Drupal\s3fs\StreamWrapper\PublicS3fsStream::stream_open" call failed in Drupal\system\Controller\JsAssetController->generateHash() (line 43 of core/lib/Drupal/Core/Asset/AssetGroupSetHashTrait.php).
My first question, is assuming this is a recent version of core, since these appear to be dynamically generated JS why are they not on assets://? After https://www.drupal.org/node/3328126 we stopped dealing with dynamic javascript creation. Is there some config somewhere that is forcing this onto public://?

Can you provide more steps to reproduce? IIRC Locale is part of a normal core standard profile install and I haven't seen this occur, though I don't usually deal with multi-lingual sites personally.

Also Drupal warns me about the the .htaccess file doesn't exists in public://and private://

I've seen that when S3FS is enabled without core having ever been deployed, or no syncing of local files to s3fs. I imagine most sites usually disable the warning in those cases as it is irrelevant to s3:// storage.

Note that all files exists in my bucket(languages/es_VDvIBCKufkGMU2tQ1_KFvYXnRgrAosuv8CW9BTOTRJQ.js, public://.htaccess and private://.htaccess).

Do they exist in the s3fs_file database table? The S3FS module relies on the database cache to determine if a file is present instead of calling out to AWS for every file requests, this could create a situation where the object exists in bucket however are not readable during normal operations.

that's cause internally drupal use @file_get_contents() to verify file contents.

IIUC file_get_contents() doesn't usually seek, it is only when provided an offset (technically PHP declares its operation undefined on remote stream wrappers though the AWS SDK does make attempts to buffer the file). That does make this a bit interesting as to why a seek is occurring here in the first place and is making the

metallized’s picture

Hey, thanks for your answer:

Do you have a s3fs_stream_open_params_alter hook that is removing the seekable option?

No

My first question, is assuming this is a recent version of core, since these appear to be dynamically generated JS why are they not on assets://? After https://www.drupal.org/node/3328126 we stopped dealing with dynamic javascript creation. Is there some config somewhere that is forcing this onto public://?

I have drupal 10.6.3 and never set $settings['file_assets_path'] in my settings.php (the line is commented)

Can you provide more steps to reproduce? IIRC Locale is part of a normal core standard profile install and I haven't seen this occur, though I don't usually deal with multi-lingual sites personally.

I don't have yet, but maybe this is the reason: first and second, these should use the assets:// stream?, btw in default locale config javascript.directory value is languages

Do they exist in the s3fs_file database table? The S3FS module relies on the database cache to determine if a file is present instead of calling out to AWS for every file requests, this could create a situation where the object exists in bucket however are not readable during normal operations.

Yeah all of them exists also in database.

metallized’s picture

I think i've identified the root cause of this issue.

The problem is in the AWS SDK StreamWrapper, not in the S3FS module itself.

Root Cause:
When using S3-compatible endpoints (like Tigris), the AWS SDK's `openReadStream()` method sets `$command['@http']['stream'] = true`, which causes the SDK to attempt seek operations on a non-seekable HTTP stream (internally it tries to do seek operations on the stream, but Guzzle's HTTP stream is not seekable by default, causing the "Stream is not seekable" error).

Temporary Workaround:
Commenting out line 734 in `StreamWrapper.php` resolves the issue.

Proper Solution:
This needs to be fixed upstream in the AWS SDK. Should i report this issue to the AWS SDK repository?.

Configuration for Tigris/S3-Compatible Services:

$config['s3fs.settings'] = [
  'use_customhost' => TRUE,
  'hostname' => 'fly.storage.tigris.dev',
  'use_path_style_endpoint' => FALSE,
  // ... other settings
];

The S3FS module may want to consider:
1. Documenting this limitation when using S3-compatible services
2. Providing a configuration option to disable streaming in the wrapper
3. Patching the AWS SDK StreamWrapper or creating a wrapper class that handles this

cmlara’s picture

Status: Active » Postponed (maintainer needs more info)

which causes the SDK to attempt seek operations on a non-seekable HTTP stream (internally it tries to do seek operations on the stream, but Guzzle's HTTP stream is not seekable by default, causing the "Stream is not seekable" error).

A few lines below seeking is enabled on non seekable streams.
https://github.com/aws/aws-sdk-php/blob/5db21cd0e5c8a5b5344596649033acf8...

Here is a sample script to prove seeking:

$filename = 'public://test2.txt';
$handle = fopen($filename, "r");
fseek($handle, 900000);
$contents = fread($handle, 200)
fseek($handle, 0);
$contents_start = fread($handle, 200);
fclose($handle);

The only aspects that doesn't test is:
Seeking very far into a file over a slow data connection, though I would expect that to be a blocking read.
The SDK is a little vague in it says it allows seeking back on data that has already been read, IIRC the SDK pulls the object from the begining, it it doesn't jump ahead so that the entire file should essentially be readable. Network speed simulation could help confirm/deny this.

https://github.com/aws/aws-sdk-php/blob/5db21cd0e5c8a5b5344596649033acf8...

I do recall early alpha releases of s3fs having some weird truncation on upload issues when the temporary storage was full, in theory something similar could happen here if the PHP temporary stream storage is full or not available (would need to investigate CachingStream)

Additionally I do somewhat recall that there multiple ways Guzzle would make connections I can't recall if it was a specific library was needed or not, perhaps this is something related to the connection type, though I would still expect the caching noted above to handle this.

Should i report this issue to the AWS SDK repository?.

Given my above comments I would hold off on reporting this to AWS. I'm not convinced yet that this is an SDK issue.

I don't have yet, but maybe this is the reason: first and second, these should use the assets:// stream?,

It is interesting that core is hard-coding the public:// path. Re-reading perhaps these are not not dynamically generated JS, or perhaps core missed them during the assets:// deployment, I'm not sure off hand. I'm not impressed that core has hard-coded the scheme, however that is an entirely different subject.

I'm going to postpone this on awaiting reproduction steps. Once you have those (preferably based on a Drupal Core Standard profile install) we can narrow down where this actually is.

You might try the sample script above( replace with the path to an object that exists in your bucket if that errors it does at least tell us something is different between your environment and my lab which would help narrow this down.

metallized’s picture

Hey, i just realize that the initial error applies to all the files (not just language/*.js or .htaccess), so i don't think is something about locale module.

I can reproduce the error following this steps:

1. Install drupal using standard profile (composer create-project drupal/recommended-project drupal)
2. Install sf3s module (composer require drupal/s3fs)
3. Modify settings.php to add bucket config:

$settings['s3fs.access_key'] = "tid_xxxxxxxxx";
$settings['s3fs.secret_key'] = "tsec_xxxxx";

$config['s3fs.settings'] = [
  'bucket' => "drupal11",
  'region' => "auto",
  'hostname' => "fly.storage.tigris.dev",
  'use_customhost' => TRUE,
  'use_https' => TRUE,
  'use_s3_for_public' => TRUE,
  'use_s3_for_private' => TRUE,
  'presigned_urls' => TRUE,
];

$settings['s3fs.use_s3_for_public'] = TRUE;
$settings['s3fs.use_s3_for_private'] = TRUE;
$settings['php_storage']['twig']['directory'] = "/tmp/php";
$config['system.file']['default_scheme'] = "s3";

4. run validate acction in admin/config/media/s3fs/actions
5. run drush s3fs:refresh-cache
6. run drush cr
7. run drush php:eval "echo file_get_contents('public://.htaccess');" (this will fail with the reported error)
8. comment out line 734 of vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php
9. run drush php:eval "echo file_get_contents('public://.htaccess');" (it works)

Drupal: 11.3.3
PHP: 8.3.6
Nginx: 1.24.0
PostgreSQL: 16.11

cmlara’s picture

Status: Postponed (maintainer needs more info) » Active

Hey, i just realize that the initial error applies to all the files (not just language/*.js or .htaccess), so i don't think is something about locale module.

That we can work with. Unfortunately your experiences do not match my local lab tests or our CI testing results.

8. comment out line 734 of vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php

I'm more interested as to what happens at line 740, does it enter the if statement and provision a CachingStream for the response body or not. Step debugging would be best, at a minimal a die('test point: Enabled caching"');should confirm if it is trying to provision caching.

metallized’s picture

That we can work with. Unfortunately your experiences do not match my local lab tests or our CI testing results.

Maybe its cause im using nginx/php-fpm?

I'm more interested as to what happens at line 740, does it enter the if statement and provision a CachingStream for the response body or not. Step debugging would be best, at a minimal a die('test point: Enabled caching"');should confirm if it is trying to provision caching.

I can't debbuging rigth now (IDE configuration) but i tested the die suggestion, the code never enters the if statement, once it get into $result = $client->execute($command);, the execution stops, no futher code is executed.

cmlara’s picture

once it get into $result = $client->execute($command);, the execution stops, no futher code is executed.

Take a look at https://www.drupal.org/project/s3fs/issues/3087363#comment-14091193 for a small snipit that can be added to the S3FS code to get some more in depth debug logs. You may be able to see some response data from the Object Store that explains the fault (streamWrappers do mask some errors by catching them and instead returning errors that they just couldn't complete the operation).

If there isn't something in those debug logs it would tend to lean towards an AWS issue.

Removing $command['@http']['stream'] = true; will not be an acceptable solution (streaming is very important for performance) however it might at least give the AWS devs some clues on what they need to look into.

Before opening an issue with the AWS SDK I would suggest adding the debug logs (they will likely need them too) and testing against an AWS S3 bucket, or at least a LocalStack development image to rule this out as a possible Tigris issue.

Maybe its cause im using nginx/php-fpm

I'm not really qualified to say it is or isn't the cause. I wouldn't expect nginix FPM to be too different from Apache FPM, however I haven't tested that so it is indeed a variable difference between the two.

metallized’s picture

Hey i got somthing like this:

-> Entering step attempt, name 'ApiCallAttemptMonitoringMiddleware'
-------------------------------------------------------------------

  no changes



<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [CONNECT]
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [FILE_SIZE_IS] message: "Content-Length: 486" bytes_max: "486"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [MIME_TYPE_IS] message: "application/octet-stream"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [PROGRESS] bytes_max: "486"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [PROGRESS] bytes_transferred: "486" bytes_max: "486"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [COMPLETED] bytes_transferred: "486" bytes_max: "486"


<- Leaving step attempt, name 'ApiCallAttemptMonitoringMiddleware'
------------------------------------------------------------------

  error was set to array(6) {
    ["instance"]=>
    string(32) "0000000000000de00000000000000000"
    ["class"]=>
    string(16) "RuntimeException"
    ["message"]=>
    string(22) "Stream is not seekable"
    ["file"]=>
    string(76) "/home/xxx/d11/vendor/guzzlehttp/psr7/src/Stream.php"
    ["line"]=>
    int(209)
    ["trace"]=>
    string(5329) "#0 /home/xxx/d11/vendor/guzzlehttp/psr7/src/Utils.php(136): GuzzleHttp\Psr7\Stream->seek()
  #1 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/CalculatesChecksumTrait.php(50): GuzzleHttp\Psr7\Utils::hash()
  #2 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/Parser/ValidateResponseChecksumResultMutator.php(118): Aws\S3\Parser\ValidateResponseChecksumResultMutator::getEncodedValue()
  #3 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/Parser/ValidateResponseChecksumResultMutator.php(79): Aws\S3\Parser\ValidateResponseChecksumResultMutator->validateChecksum()
  #4 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/Parser/S3Parser.php(203): Aws\S3\Parser\ValidateResponseChecksumResultMutator->__invoke()
  #5 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/Parser/S3Parser.php(83): Aws\S3\Parser\S3Parser->executeS3ResultMutators()
  #6 /home/xxx/d11/vendor/aws/aws-sdk-php/src/WrappedHttpHandler.php(126): Aws\S3\Parser\S3Parser->__invoke()
  #7 /home/xxx/d11/vendor/aws/aws-sdk-php/src/WrappedHttpHandler.php(93): Aws\WrappedHttpHandler->parseResponse()
  #8 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(209): Aws\WrappedHttpHandler->Aws\{closure}()
  #9 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(158): GuzzleHttp\Promise\Promise::callHandler()
  #10 /home/xxx/d11/vendor/guzzlehttp/promises/src/TaskQueue.php(52): GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}()
  #11 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\Promise\TaskQueue->run()
  #12 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\Promise\Promise->invokeWaitFn()
  #13 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\Promise\Promise->waitIfPending()
  #14 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\Promise\Promise->invokeWaitList()
  #15 /home/xxx/d11/vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\Promise\Promise->waitIfPending()
  #16 /home/xxx/d11/vendor/aws/aws-sdk-php/src/AwsClientTrait.php(58): GuzzleHttp\Promise\Promise->wait()
  #17 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php(735): Aws\AwsClient->execute()
  #18 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php(160): Aws\S3\StreamWrapper->openReadStream()
  #19 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php(965): Aws\S3\StreamWrapper->Aws\S3\{closure}()
  #20 /home/xxx/d11/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php(158): Aws\S3\StreamWrapper->boolCall()
  #21 /home/xxxx/d11/web/modules/contrib/s3fs/src/StreamWrapper/S3fsStream.php(712): Aws\S3\StreamWrapper->stream_open()
  #22 [internal function]: Drupal\s3fs\StreamWrapper\S3fsStream->stream_open()
  #23 /home/xxx/d11/vendor/drush/drush/src/Commands/core/PhpCommands.php(33) : eval()'d code(1): file_get_contents()
  #24 /home/xxx/d11/vendor/drush/drush/src/Commands/core/PhpCommands.php(33): eval()
  #25 [internal function]: Drush\Commands\core\PhpCommands->evaluate()
  #26 /home/xxx/d11/vendor/consolidation/annotated-command/src/CommandProcessor.php(276): call_user_func_array()
  #27 /home/xxx/d11/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
  #28 /home/xxxx/d11/vendor/consolidation/annotated-command/src/CommandProcessor.php(175): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
  #29 /home/xxxx/d11/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(389): Consolidation\AnnotatedCommand\CommandProcessor->process()
  #30 /home/xxx/d11/vendor/symfony/console/Command/Command.php(341): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
  #31 /home/xxx/d11/vendor/symfony/console/Application.php(1102): Symfony\Component\Console\Command\Command->run()
  #32 /home/xxx/d11/vendor/drush/drush/src/Application.php(201): Symfony\Component\Console\Application->doRunCommand()
  #33 /home/xxx/d11/vendor/symfony/console/Application.php(356): Drush\Application->doRunCommand()
  #34 /home/xxx/d11/vendor/symfony/console/Application.php(195): Symfony\Component\Console\Application->doRun()
  #35 /home/xxx/d11/vendor/drush/drush/src/Runtime/Runtime.php(113): Symfony\Component\Console\Application->run()
  #36 /home/xxx/d11/vendor/drush/drush/src/Runtime/Runtime.php(40): Drush\Runtime\Runtime->doRun()
  #37 /home/xxx/d11/vendor/drush/drush/drush.php(140): Drush\Runtime\Runtime->run()
  #38 /home/xxx/d11/vendor/bin/drush.php(119): include('...')
  #39 {main}"
  }

  Inclusive step time: 0.8622088432312

So GuzzleHttp\Psr7\Utils::hash() tries to seek() the $response->getBody() and fires the error.

I tried to install the awscrt mod in php and got something like this:

-> Entering step attempt, name 'ApiCallAttemptMonitoringMiddleware'
-------------------------------------------------------------------

  no changes



<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [CONNECT]
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [FILE_SIZE_IS] message: "Content-Length: 486" bytes_max: "486"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [MIME_TYPE_IS] message: "application/octet-stream"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [PROGRESS] bytes_max: "486"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [PROGRESS] bytes_transferred: "486" bytes_max: "486"
<GET https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess> [COMPLETED] bytes_transferred: "486" bytes_max: "486"


<- Leaving step attempt, name 'ApiCallAttemptMonitoringMiddleware'
------------------------------------------------------------------

  result was set to array(2) {
    ["instance"]=>
    string(32) "0000000000000dde0000000000000000"
    ["data"]=>
    array(11) {
      ["Body"]=>
      object(GuzzleHttp\Psr7\Stream)#3351 (7) {
        ["stream":"GuzzleHttp\Psr7\Stream":private]=>
        resource(1533) of type (stream)
        ["size":"GuzzleHttp\Psr7\Stream":private]=>
        NULL
        ["seekable":"GuzzleHttp\Psr7\Stream":private]=>
        bool(false)
        ["readable":"GuzzleHttp\Psr7\Stream":private]=>
        bool(true)
        ["writable":"GuzzleHttp\Psr7\Stream":private]=>
        bool(false)
        ["uri":"GuzzleHttp\Psr7\Stream":private]=>
        string(61) "https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess"
        ["customMetadata":"GuzzleHttp\Psr7\Stream":private]=>
        array(0) {
        }
      }
      ["AcceptRanges"]=>
      string(5) "bytes"
      ["LastModified"]=>
      object(Aws\Api\DateTimeResult)#3345 (3) {
        ["date"]=>
        string(26) "2026-02-15 09:37:36.000000"
        ["timezone_type"]=>
        int(2)
        ["timezone"]=>
        string(3) "GMT"
      }
      ["ContentLength"]=>
      int(486)
      ["ETag"]=>
      string(34) ""1c79eba61b05143290a34dc5b12ec7ec""
      ["ChecksumCRC32"]=>
      string(8) "feINTA=="
      ["ChecksumType"]=>
      string(11) "FULL_OBJECT"
      ["ContentType"]=>
      string(24) "application/octet-stream"
      ["Metadata"]=>
      array(0) {
      }
      ["ChecksumValidated"]=>
      string(5) "CRC32"
      ["@metadata"]=>
      array(4) {
        ["statusCode"]=>
        int(200)
        ["effectiveUri"]=>
        string(61) "https://drupal11.fly.storage.tigris.dev/s3fs-public/.htaccess"
        ["headers"]=>
        array(22) {
          ["accept-ranges"]=>
          string(5) "bytes"
          ["access-control-allow-headers"]=>
          string(0) ""
          ["access-control-allow-methods"]=>
          string(3) "GET"
          ["access-control-allow-origin"]=>
          string(1) "*"
          ["access-control-expose-headers"]=>
          string(0) ""
          ["access-control-max-age"]=>
          string(5) "80000"
          ["content-length"]=>
          string(3) "486"
          ["content-type"]=>
          string(24) "application/octet-stream"
          ["etag"]=>
          string(34) ""1c79eba61b05143290a34dc5b12ec7ec""
          ["last-modified"]=>
          string(29) "Sun, 15 Feb 2026 09:37:36 GMT"
          ["server"]=>
          string(9) "Tigris OS"
          ["server-timing"]=>
          string(33) "total;dur=1,cache;desc=read;dur=1"
          ["x-amz-acl"]=>
          string(11) "public-read"
          ["x-amz-checksum-crc32"]=>
          string(8) "feINTA=="
          ["x-amz-checksum-type"]=>
          string(11) "FULL_OBJECT"
          ["x-amz-content-sha256"]=>
          string(64) "28039dffc5bcf9de06c999f11f9a6c3372bcf1c675fcca2d6e5773680e281061"
          ["x-amz-date"]=>
          string(16) "20260215T093734Z"
          ["x-amz-request-id"]=>
          string(19) "1771201252632158474"
          ["x-tigris-regions"]=>
          string(3) "gru"
          ["x-tigris-served-from"]=>
          string(3) "gru"
          ["date"]=>
          string(29) "Mon, 16 Feb 2026 00:20:52 GMT"
          ["connection"]=>
          string(5) "close"
        }
        ["transferStats"]=>
        array(0) {
        }
      }
    }
  }

  Inclusive step time: 0.72616600990295

With awscrt module enable the error dissapears but also i can't get nothing from stream (is empty).

I think i will patch the aws sdk php to remove the stream and use sink instead.

cmlara’s picture

Title: Stream wrapper fails on file_get_contents() due to non-seekable stream » Stream wrapper fails on file_get_contents() due to non-seekable stream (Tigris)
Status: Active » Closed (works as designed)
Issue tags: -locale, -file_get_contents, -stream

Stack trace agrees with your report regarding stalling out at execute().

Closing this since there doesn't appear at the moment to be any indication this is caused by failures on S3FS or anything we can reasonably solve inside of S3FS. Strictly speaking yes S3FS could re-write the AWS StreamWrapper::stream_open() method, however in S3FS we would want streaming to be present (it is partly why we don't have proxy support yet) and would want all of AWS Validators to still process (it appears this flaw is inside one one of the validators), we would essentially want the existing implementation.

I wouldn't be surprised if the root cause of this is an API "breach" by Tigris that combined with a bug in the SDK (in such a case the SDK team might defer it to Tigris as IIUC they don't officially support non AWS targets).

I will mention #3513322: Backblaze B2 - Unsupported header 'x-amz-checksum-crc32' had some information regarding disabling checksums (was focused on uploads however IIRC there are also response processing options). Perhaps those related configs for the SDK may provide another alternative.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

metallized’s picture

I think its valuable for S3FS the option to disabled check validations, i make this patch which solves my errors, also need to adding this to my settings.php:

$config['s3fs.settings']['response_checksum_validation'] = 'when_required';

Thanks for your help, up to you what is next.