Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S3 unexpected error forbidden: null thrown #291

Closed
andrewrk opened this issue Jun 4, 2014 · 26 comments
Closed

S3 unexpected error forbidden: null thrown #291

andrewrk opened this issue Jun 4, 2014 · 26 comments

Comments

@andrewrk
Copy link

andrewrk commented Jun 4, 2014

This error is thrown from inside the library and there is no way for me to handle the error. Also I have reason to believe that it is invalid and I should not be receiving a forbidden null error.

/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/request.js:34
        throw e;
              ^
Forbidden: null
    at Request.extractError (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/services/s3.js:251:35)
    at Request.callListeners (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/sequential_executor.js:114:20)
    at Request.callListeners (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/sequential_executor.js:115:16)
    at Request.emit (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/sequential_executor.js:81:10)
    at Request.emit (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/request.js:578:14)
    at Request.transition (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/request.js:12:12)
    at AcceptorStateMachine.runTo (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/state_machine.js:28:10
    at Request.<anonymous> (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/request.js:28:9)
    at Request.<anonymous> (/home/andy/dev/node-s3-cli/node_modules/s3/node_modules/aws-sdk/lib/request.js:580:12)

2.0.0-rc.18

@andrewrk andrewrk changed the title unexpected error forbidden: null thrown S3 unexpected error forbidden: null thrown Jun 4, 2014
@lsegal lsegal added bug labels Jun 4, 2014
@lsegal
Copy link
Contributor

lsegal commented Jun 4, 2014

Can you explain what you mean by "there is no way for me to handle the error"? The Forbidden error is generated by S3 for resources that you do not have access to, so this is the correct error to return. I suppose the issue here is that the error is not being returned properly to the callback?

Can you provide code that reproduces your issue along with your expected result? Also, what version of the SDK are you using?

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

Can you explain what you mean by "there is no way for me to handle the error"?

I mean that the error is thrown instead of returned in a callback or event. And that it is thrown in a function that I don't call directly so try/catch won't work.

The Forbidden error is generated by S3 for resources that you do not have access to, so this is the correct error to return

I am running code that is calling putObject thousands of times, and several thousand times it works, followed by one time that I get this error.

Can you provide code that reproduces your issue along with your expected result?

Not reliably. It seems to be a sporadic error.

Here is the code I am running: https://github.com/andrewrk/node-s3-client/blob/master/index.js#L407

Doing it on a folder with ~6,000 files each about 5 MB.

Also, what version of the SDK are you using?

Version 2.0.0-rc.18. I've updated now and will try to reproduce with the latest.

@lsegal
Copy link
Contributor

lsegal commented Jun 4, 2014

a Forbidden error with no body only comes back from requests where the server is not allowed to return a body. In this context, that usually means a HEAD request. That said, I don't see any calls to headObject in your code, so this is a little odd. If you get a 403 response on any other request (like GET or PUT), S3 should be returning a body with an error, and the SDK should be parsing this out to show "AccessDenied" as the error code instead. Forbidden is just our fallback when there is no body.

Can you enable logging in the SDK so we can capture the requests being made? AWS.config.logger = process.stdout at the top of the script would do it. I'm also curious if you know why you are getting 403 errors in the first place? If you are uploading objects to a bucket you own, you should not be getting these errors.

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

I turned on the logger and here's the file http://superjoe.s3.amazonaws.com/temp/s3debug-out.txt

On this run I got no error but instead the program stalled, as if for some reason a callback wasn't being called. This particular error on this run is probably a bug in my code. I'll do another run.

What I noticed about this log file is that the response codes start out as 200s and then they turn into 403s at the end. Same credentials and everything. I also noticed that the time is monotonically increasing. If that is request time, then perhaps what is happening is that I'm starting a bazillion requests at once, and then taking too long to fulfill them. Would a 403 error be given in that situation?

My understanding is that I should not worry about starting as many requests as possible, because the global agent socket pooling will make sure that only N number of them will run at once.

@lsegal
Copy link
Contributor

lsegal commented Jun 4, 2014

Yes. The 403 errors are likely signing errors caused by the signing time expiring. I would recommend reading through #221 and making sure you're only opening at most maxSockets connections at a time. The SDK does some things to mitigate time skew errors, but it may be that we are not doing something correctly, so keeping track of your own request queue would avoid those problems.

That said I don't see an error being thrown out of the run loop either. Are you still reproducing this behavior at all?

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

I'm in the middle of another run. In this one the number of concurrent putObject calls is limited to 20 and the global maxSockets number is 30. So far the log is up to 363 seconds for each request, still monotonically increasing, and number of putObject calls is at 158. How can request times still be increasing?

@lsegal
Copy link
Contributor

lsegal commented Jun 4, 2014

Request times increase in the logger because we log from the start time when the request was initiated. Since you constructed the requests at the same time, they all have the same start time. Those logs don't show the "time to send" data, but rather, time from construction.

Can you print the full error message for your 403 responses? It would be interesting to know exactly why those requests fail.

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

In this run I am not constructing all multiple-thousand requests at the same time, only 20 at a time. Yet the logger is printing a monotonically increasing time.

This run completed and it had the same behavior where it stalled, as if a callback were never called. That's 4 in a row with the same behavior after upgrading to rc19.

I think after upgrading to rc19 I'm not seeing the issue that I filed originally. Instead what I think is happening is I'm not getting a callback from putObject. The code is set up to crash and print a stack trace if I get an error from putObject, and that is not happening.

I'm doing another run where I will only call putObject one at a time, with output that will enable us to make sure the callback is actually being called.

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

OK I have verified that there is clearly a bug in my code that manages when to call putObject. It was not throttling it like I thought it was.

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

Ah ok. No bug, I just put the configuration value in the wrong spot for this comment: #291 (comment)

Now I'm doing the real test with putObject only allowed to have 20 requests at a time.

@andrewrk
Copy link
Author

andrewrk commented Jun 4, 2014

Maybe this issue should be merged with #290 but the results of this run is that the putObject callback was called twice. Results here: #290 (comment)

@lsegal
Copy link
Contributor

lsegal commented Jun 13, 2014

#290 was fixed and is part of the latest release-- can you verify whether you still have problems related to this issue, or if the fix in the other issue resolves this one too?

@andrewrk
Copy link
Author

I just ran my test case again with 2.0.0-rc.20. The test case is uploading a directory of files that totals to 46 GB to S3. 35% of the way through I started getting AWS SDK JS called callback twice warnings and then progress halted. No error was thrown, and the process looks like it was hanging.

Progress: 17630093853/49834727653 35% 11.94 MB/s                    Warning: AWS SDK JS called callback twice.

@lsegal
Copy link
Contributor

lsegal commented Jun 19, 2014

@andrewrk I've been running a similar test a number of times over the day but cannot seem to reproduce multiple callbacks. The code I'm running is:

var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var fs = require('fs');
require('https').Agent.maxSockets = 0;

var num = 0;
for (var i = 0; i < 100; i++) {
  (function(x) {
    var callbackCalled = false;
    var file = fs.createReadStream('bigfile');
    s3.putObject({Bucket:'mybucket',Key:'big/bigfile'+x,Body:file}, function(err, data) {
      if (callbackCalled) { console.log("!!! Duplicate callback!", x); }
      callbackCalled = true;
      console.log(x, err,data);
      if (!err) { num++; if (num >= 100) console.log("Completed."); }
    });
  })(i);
}

bigfile is a 200mb file that I am uploading in 100 parallel requests. Over 10+ runs I only managed to reproduce one RequestTimeout error, but never managed to halt the callback queue or print "Duplicate callback!".

I'm curious what you get if you try the above code. You can create a file bigfile or use one of your existing files to test this.

@andrewrk
Copy link
Author

I'm able to get the duplicate callback with a modified test case:

require('graceful-fs'); // v2.0.3

var AWS = require('aws-sdk');
var s3 = new AWS.S3({
  sslEnabled: false,
});
var fs = require('fs');
var Pend = require('pend');
require('https').Agent.maxSockets = 30;
require('http').Agent.maxSockets = 30;

var pend = new Pend();
var findit = require('findit');
var walker = findit("/home/andy/music/");

walker.on('file', function(file) {
  pend.go(function(cb) {
    var params = {
      Bucket: process.env.S3_BUCKET,
      Key: "1tmp1/" + file,
      Body: fs.createReadStream(file),
    };
    var callbackCalled = false;
    s3.putObject(params, function(err, data) {
      if (callbackCalled) {
        console.log("!!! Duplicate callback!", file);
      }
      callbackCalled = true;
      if (err && err.retryable) {
        console.log("!!! Retryable error!", file);
      }
      console.log(file, err, data);
      cb();
    });
  });
});

walker.on('end', function() {
  pend.wait(function() {
    console.log("done");
  });
});
...
!!! Duplicate callback! /home/andy/music/50 Best Trance & Progressive Tunes Of 2012/(Disc 2) 25 - Darker Shades Of Black (Original Mix).mp3
/home/andy/music/50 Best Trance & Progressive Tunes Of 2012/(Disc 2) 25 - Darker Shades Of Black (Original Mix).mp3 { [RequestTimeTooSkewed: The difference between the request time and the current time is too large.]
  message: 'The difference between the request time and the current time is too large.',
  code: 'RequestTimeTooSkewed',
  time: Thu Jun 19 2014 16:44:43 GMT+0000 (EDT),
  statusCode: 403,
  retryable: false } null
...

This re-introduces the problem of requests timing out, but even in that situation the callback should never be called twice.

I'll work on trying to reduce this test case further, notably removing the graceful-fs dependency.

@andrewrk
Copy link
Author

OK here's a test case without graceful-fs:

var AWS = require('aws-sdk');
var s3 = new AWS.S3({
  sslEnabled: false,
});
var fs = require('fs');
var Pend = require('pend');
require('https').Agent.maxSockets = 30;
require('http').Agent.maxSockets = 30;

var pend = new Pend();
pend.max = 500;
var findit = require('findit');
var walker = findit("/home/andy/music/");

walker.on('file', function(file) {
  pend.go(function(cb) {
    var params = {
      Bucket: process.env.S3_BUCKET,
      Key: "1tmp1/" + file,
      Body: fs.createReadStream(file),
    };
    var callbackCalled = false;
    s3.putObject(params, function(err, data) {
      if (callbackCalled) {
        console.log("!!! Duplicate callback!", file);
      }
      callbackCalled = true;
      if (err && err.retryable) {
        console.log("!!! Retryable error!", file);
      }
      console.log(file, err, data);
      cb();
    });
  });
});

walker.on('end', function() {
  pend.wait(function() {
    console.log("done");
  });
});

I get the first duplicate callback after the 4th log statement is printed for a RequestTimeTooSkewed.

!!! Duplicate callback! /home/andy/music/50 Best Trance & Progressive Tunes Of 2012/(Disc 2) 08 - Cupid's Casualty (Mike Saint-Jules Remix).mp3
/home/andy/music/50 Best Trance & Progressive Tunes Of 2012/(Disc 2) 08 - Cupid's Casualty (Mike Saint-Jules Remix).mp3 { [RequestTimeTooSkewed: The difference between the request time and the current time is too large.]
  message: 'The difference between the request time and the current time is too large.',
  code: 'RequestTimeTooSkewed',
  time: Thu Jun 19 2014 13:12:16 GMT-0400 (EDT),
  statusCode: 403,
  retryable: false } null

@andrewrk
Copy link
Author

Same thing, except this time I used SSL and set maxSockets to 0 like you did.

var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var fs = require('fs');
var Pend = require('pend');
require('https').Agent.maxSockets = 0;

var pend = new Pend();
pend.max = 500;
var findit = require('findit');
var walker = findit("/home/andy/music/");

walker.on('file', function(file) {
  pend.go(function(cb) {
    var params = {
      Bucket: process.env.S3_BUCKET,
      Key: "1tmp1/" + file,
      Body: fs.createReadStream(file),
    };
    var callbackCalled = false;
    s3.putObject(params, function(err, data) {
      if (callbackCalled) {
        console.log("!!! Duplicate callback!", file);
      }
      callbackCalled = true;
      if (err && err.retryable) {
        console.log("!!! Retryable error!", file);
      }
      console.log(file, err, data);
      cb();
    });
  });
});

walker.on('end', function() {
  pend.wait(function() {
    console.log("done");
  });
});
...
!!! Duplicate callback! /home/andy/music/Andy Kelley/Airplane.mp3
!!! Duplicate callback! /home/andy/music/Beatport Progressive House Top 100 February/Thomas Gold, Kaelyn Behr - Remember (Original MIx).mp3
!!! Duplicate callback! /home/andy/music/Beatport Progressive House Top 100 February/Tritonal, Paris Blohm, Sterling Fox - Colors (Original Mix).mp3
!!! Duplicate callback! /home/andy/music/CHVRCHES - The Bones of What You Believe [Deluxe Version] (2013)/07 Recover.mp3
!!! Duplicate callback! /home/andy/music/CHVRCHES - The Bones of What You Believe [Deluxe Version] (2013)/08 Night Sky.mp3
!!! Duplicate callback! /home/andy/music/D-Vision/It's a Dream (D-Vision vs East Clubbers Remix).mp3
...

@andrewrk
Copy link
Author

Next test. This one sets pend.max to 4 instead of 500, to avoid RequestTimeTooSkewed errors:

var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var fs = require('fs');
var Pend = require('pend');
require('https').Agent.maxSockets = 0;

var pend = new Pend();
pend.max = 4;
var findit = require('findit');
var walker = findit("/home/andy/music/");

walker.on('file', function(file) {
  pend.go(function(cb) {
    var params = {
      Bucket: process.env.S3_BUCKET,
      Key: "1tmp1/" + file,
      Body: fs.createReadStream(file),
    };
    var callbackCalled = false;
    s3.putObject(params, function(err, data) {
      if (callbackCalled) {
        console.log("!!! Duplicate callback!", file);
      }
      callbackCalled = true;
      if (err && err.retryable) {
        console.log("!!! Retryable error!", file);
      }
      console.log(file, err, data);
      cb();
    });
  });
});

walker.on('end', function() {
  pend.wait(function() {
    console.log("done");
  });
});

This one took a lot longer before I saw any duplicate callbacks.

Here's the first time I got a callback for this certain file:

/home/andy/music/057 Children Of Bodom/001 Follow the Reaper/002 Taste of My Scythe.mp3 { [RequestTimeout: Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.]
  message: 'Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.',
  code: 'RequestTimeout',
  time: Thu Jun 19 2014 15:46:06 GMT-0400 (EDT),
  statusCode: 400,
  retryable: false } null

And then later:

!!! Duplicate callback! /home/andy/music/057 Children Of Bodom/001 Follow the Reaper/002 Taste of My Scythe.mp3
/home/andy/music/057 Children Of Bodom/001 Follow the Reaper/002 Taste of My Scythe.mp3 { [RequestTimeout: Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.]
  message: 'Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.',
  code: 'RequestTimeout',
  time: Thu Jun 19 2014 15:46:47 GMT-0400 (EDT),
  statusCode: 400,
  retryable: false } null

@lsegal
Copy link
Contributor

lsegal commented Jun 19, 2014

I just ran this test over a multiple test of runs with a set of "bigfile" files (20 of them, 200mb each), but could not manage to make this fail once.

~$ AWS_REGION=us-west-2 S3_BUCKET=mybucket node test
/Users/lsegal/tmpupload/bigfile13 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile1 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile12 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile11 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile14 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile10 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile17 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile15 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile18 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile16 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile19 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile2 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile4 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile20 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile3 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile5 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile9 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile7 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile6 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
/Users/lsegal/tmpupload/bigfile8 null { ETag: '"05a5c8d669ccb39a7a9f4723a8271881"' }
done

I tried a various set of regions too, along with modifying the maxSockets from 0 (unlimited) to 3 (a low value).

Are you sure you are using the rc.20 release? The RequestTimeTooSkewed error was previously resolved in an earlier RC, so I'm a little surprised to see that popping up.

Note: I'm not doubting that you are getting this error, but without being able to reproduce this myself, it will be hard for me to investigate what the fix is. If you can provide any more information about how you might be reproducing this issue I can do some more investigating to see if I can get this failing on my end.

@andrewrk
Copy link
Author

Are you sure you are using the rc.20 release?

Yep, 2.0.0-rc.20.

On Monday I'll see if I can get another test case going which is independent of the particular files on my computer.

lsegal added a commit that referenced this issue Jun 20, 2014
Only allow either the HTTP loading or error callback to be fired,
but never both. Without this guard, it is possible for both
callbacks to fire and trigger multiple branches of the request
lifecycle, culminating in multiple 'complete' callbacks.

References #291, #300
@lsegal
Copy link
Contributor

lsegal commented Jun 20, 2014

@andrewrk I've been working with @BrandonCopley in #300 on a similar issue where callbacks are firing more than once. This behavior only seems to be related (at least for me) to timeouts, but it might be worth testing against the above commit which should resolve multiple callbacks being fired when timeouts occur.

@lsegal
Copy link
Contributor

lsegal commented Jun 23, 2014

Any response based on the above changes?

@andrewrk
Copy link
Author

Running a test now.

@andrewrk
Copy link
Author

With that commit I no longer get callback called twice. I do occasionally get a RequestTimeout which is a retryable error, but that is expected. Glad you found the fix!

@lsegal
Copy link
Contributor

lsegal commented Jun 24, 2014

Glad to hear this is working for you @andrewrk! The patch will be out with our next release.

@lock
Copy link

lock bot commented Sep 30, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants