S3 RequestTimeTooSkewed: Fix the Clock That Signed It

Logeshwaran
—

The S3 error RequestTimeTooSkewed means the clock on the machine that signed your request is more than 15 minutes away from S3's own clock, ahead or behind, so the fix is to put that machine on automatic time and send the request again. Here's the twist: 15 minutes is a wide gap, wider than the five minutes that applies to most AWS services, so a clock that merely looks a little off on your screen is rarely the culprit. The clock that counts is the one inside the shell, virtual machine, container or server that actually sent the request.

⚡ Quick Answer

• Console → S3 has no clock setting to change. Fix the time on the machine that sends the request (Windows: Control Panel → Date and Time → Internet Time; macOS: Date & Time → set automatically; Linux: chrony). For a clean second opinion, open AWS CloudShell from the console navigation bar and run the same command there.

• CLI → date -u, then curl -sI https://s3.amazonaws.com | grep -i '^date'. If the two times are more than 900 seconds apart, in either direction, that is your error.

If you only read this box: sync the sending machine to time.aws.com (or 169.254.169.123 on EC2), then rerun the command. The details live in the one-minute check and the fix for each operating system.

Jake's nightly backup of the day's repair photos, into a bucket named after a cat called Biscuit who has since retired, stopped landing on a Tuesday. He found out on Friday, when a customer disputed a $180 screen-repair warranty claim and the "before" photo Jake had promised to have on file wasn't there. The backup script's log had been saying the same thing three nights running: An error occurred (RequestTimeTooSkewed) when calling the PutObject operation: The difference between the request time and the current time is too large.

Ethan read it over his shoulder. "It's a clock, Jake. Every request you send to S3 carries a timestamp, and S3 compares that stamp with its own clock. Think of a movie ticket printed with the time. If the stamp is more than 15 minutes off from the theater's clock, the usher turns you away. Not because he thinks you're a thief, but because he can't tell your ticket from an old one somebody kept in a pocket."

The exact RequestTimeTooSkewed error text, and every costume it wears

People search for this error in a dozen different shapes, because every tool wraps it differently. Underneath the wrapping it is always the same thing: the error code is RequestTimeTooSkewed, the HTTP status is 403 Forbidden, and the description S3 publishes reads "The difference between the request time and the server's time is too large." The message S3 puts in the response body is worded slightly differently: The difference between the request time and the current time is too large. Same error, two phrasings.

Here is the shape of the XML that comes back. The three lines in the middle are the whole diagnosis, so it's worth learning to read them.

<Error>
  <Code>RequestTimeTooSkewed</Code>
  <Message>The difference between the request time and the current time is too large.</Message>
  <RequestTime>20150604T232559Z</RequestTime>
  <ServerTime>2015-06-04T22:26:16Z</ServerTime>
  <MaxAllowedSkewMilliseconds>900000</MaxAllowedSkewMilliseconds>
  <RequestId>...</RequestId>
  <HostId>...</HostId>
</Error>

RequestTime is the timestamp your machine stamped on the request, in UTC (Coordinated Universal Time, the world's reference clock that has no time zone or daylight-saving changes). ServerTime is what S3's clock said when the request arrived. MaxAllowedSkewMilliseconds is the limit: 900,000 milliseconds, which is 900 seconds, which is 15 minutes. In the sample above, the request claims 23:25:59 while S3 says 22:26:16, a gap of 59 minutes and 43 seconds, close to a round hour, which looks more like a time-zone slip than slow drift. One quirk: RequestTime doesn't always use that compact format. Older clients that sign with the HTTP Date header produce responses where it reads like Mon, 18 Jan 2016 14:51:35 GMT. Same field, same subtraction.

Where you see it How it reads What to copy out
AWS CLI An error occurred (RequestTimeTooSkewed) when calling the CreateMultipartUpload operation: The difference between the request time and the current time is too large. Run again with --debug to get the two times and the request IDs
Python (boto3) botocore.exceptions.ClientError: An error occurred (RequestTimeTooSkewed) when calling the ListBuckets operation The error code inside the exception's response
PHP SDK Aws\S3\Exception\S3Exception ... Client error: 403 RequestTimeTooSkewed (client) The XML body, which carries RequestTime and ServerTime
Ruby SDK Aws::S3::Errors::RequestTimeTooSkewed The exception message plus request ID
Java SDK v2 S3Exception whose error code is RequestTimeTooSkewed The status code, error code and extended request ID
Backup and sync software The same words inside the vendor's own wrapper, often ending in error code: 403 Whichever log line names the code, then check the clock on the machine that runs the backup job

Notice what is missing from that table: the operation name doesn't matter. ListBuckets, PutObject, UploadPart and CreateMultipartUpload (the piece-by-piece upload S3 uses for big files) all fail identically, because the timestamp check isn't about what you asked for. It is about when you asked.

🙋‍♂️ Jake's Reality Check

"It says 403. Doesn't 403 mean somebody changed my bucket permissions?"

The straight answer. Not this time. S3 files RequestTimeTooSkewed under 403 right next to SignatureDoesNotMatch, InvalidAccessKeyId and AccountProblem, which is why it looks like a permissions problem at a glance. It isn't. Your bucket policy, permissions and keys never got a vote; the request was turned away at the timestamp.

The 15-minute rule, and why AWS's own pages seem to disagree about it

Every request you send to S3 is signed. A signature is a fingerprint computed from your secret access key, the details of the request and the time. The method the CLI and SDKs use is called Signature Version 4, or SigV4. The signed portions of a request are valid within 15 minutes of the timestamp in that request, and S3 compares each request's timestamp with its own clock at the moment the request arrives. The rule exists so that someone who captures a signed request can't replay it later.

The rule is about the difference between two clocks, so it works in both directions. A clock that runs 16 minutes slow fails. A clock that runs 16 minutes fast fails just as hard. Nothing in the rule mentions your Region, your account or which bucket you're touching. It compares your request's timestamp with S3's clock, full stop.

Now the confusing part. For most AWS services, a request must reach AWS within five minutes of its timestamp. For S3 it's 15. Both numbers are current as of October 1, 2026, and they aren't fighting: five minutes is the common rule, and S3 is the more forgiving one. If you're chasing a signature-time failure in some other service, use the smaller number. If you're here because of the S3 error, 15 minutes (900 seconds) is the line.

Time limit people mix up Value Scope Error you get when you break it
S3 request timestamp window 15 minutes (900 seconds), ahead or behind Per request RequestTimeTooSkewed (403)
General SigV4 replay window Five minutes in most cases Per request, other AWS services Messages such as Signature expired or Signature not yet current
Presigned URL lifetime (X-Amz-Expires) 1 second up to 604,800 seconds (seven days) Per URL AccessDenied with Request has expired
Presigned URL made in the S3 console 1 minute to 12 hours Per URL Same as above
Presigned URL from aws s3 presign 3,600 seconds (one hour) by default, up to 604,800 with --expires-in Per URL Same as above
Time skew that breaks domain logins on Windows More than five minutes Per domain-joined instance Login trouble, not an S3 error at all

"Don't memorize both numbers," Ethan told Jake. "Remember that S3 forgives 15 minutes, and that nothing in your setup should ever come near using that forgiveness. A healthy machine is off by milliseconds. If yours is off by minutes, something has stopped doing its job."

✅ Why "just change the time zone" is the wrong advice

The signature uses UTC, the same instant everywhere on Earth. Changing your time zone changes how the clock is displayed, not what instant it is, so it can't fix a wrong clock and a wrong zone can't break a right one. Two exceptions, both covered below: a hand-written signer that forgets to convert local time to UTC, and a Windows instance whose time zone was changed and then restarted.

One check that tells you which clock is wrong

Before you touch a setting, measure. It turns "my clock is probably off" into a number. The only trick is doing it in the right place.

  1. Open a shell on the exact machine that runs the failing command. That means the server, the virtual machine, the container or the laptop where the script actually runs, not the desk you're sitting at and not the machine you connected from.
  2. Print that machine's time in UTC. On Linux or macOS, run date -u. On Windows PowerShell, run (Get-Date).ToUniversalTime().
  3. Ask S3 what time it thinks it is. Run curl -sI https://s3.amazonaws.com | grep -i '^date'. On Windows, use curl.exe -sI https://s3.amazonaws.com and look for the Date line. S3 puts the date and time it responded in that header, so any S3 response is a free clock reading, whatever its status code.
  4. Subtract. If the two readings are more than 900 seconds apart, either way round, you've found your error.
  5. Or skip steps 2 to 4 if you have the error XML: subtract RequestTime from ServerTime. The answer is already in the response.

If the gap is under 900 seconds and the error persists, you're measuring the wrong machine, which is why the seventh row of the next table exists.

Cause How you can tell Fix
1. Automatic time is switched off Gap is a few minutes to a few hours and keeps growing Turn on automatic time; see the OS steps
2. The time daemon has no working source chronyc sources -v shows no line starting with ^* Point it at 169.254.169.123 on EC2 or time.aws.com elsewhere
3. The VM was restored, paused or switched time sources The clock jumped suddenly, forward or back Force a resync (w32tm /resync /rediscover on Windows, restart chronyd on Linux)
4. Windows instance time zone changed, then restarted Offset roughly equals the zone difference Put the instance back on UTC and resync
5. Hand-written signing code treats local time as UTC Gap is exactly your UTC offset, such as 5 hours 30 minutes or 8 hours Convert to UTC before formatting X-Amz-Date, or use an SDK
6. The endpoint isn't Amazon's The host in --debug output doesn't end in amazonaws.com You're talking to a look-alike service; check that server's clock and its rules
7. You checked one clock and the signing happens on another Your desk looks right, the error insists otherwise Run date -u inside the same shell, VM or container as the failing command

If I had to bet, I'd bet on rows 1 and 7: switched-off automatic time is the boring default, and modern setups put an extra layer of machine between you and the clock.

🙋‍♂️ Jake's Reality Check

"Wait. So my computer's clock is a security credential? It can't even keep the shop's opening hours in sync with my phone."

The straight answer. Sort of, yes. The timestamp is part of what proves the request is fresh, so a clock that's wrong by a quarter of an hour invalidates an otherwise perfect set of keys. Automatic time fixes it for good, and it costs nothing.

Fix the clock on Windows, macOS and Linux (the console route)

There is no S3 console setting for this, and no bucket property either, so "console route" here means two things: the date and time screens on the machine that sends the request, and the AWS Management Console as a clean second opinion. For that second opinion, choose the CloudShell icon in the console's top navigation bar, or open https://console.aws.amazon.com/cloudshell/. CloudShell is a browser-based shell that comes signed in with your console credentials and has the AWS CLI already installed. Run the same S3 command there. If it works, your bucket, permissions and keys are fine and the clock on your own machine is the one to fix. That's comforting to know at 11 p.m.

Machine Where to change it Time source to use How to confirm
Windows PC (not in a domain) Control Panel → Date and Time → Internet Time tab → Change settings → check Synchronize with an Internet time server time.aws.com in the Server box w32tm /query /status
Windows EC2 instance Command Prompt: w32tm /config /manualpeerlist:169.254.169.123 /syncfromflags:manual /update 169.254.169.123, which most Amazon Windows AMIs already use w32tm /query /configuration, then look at NtpServer
macOS Date & Time settings (System Settings, or System Preferences on older versions) → set date and time automatically time.aws.com in the source box date -u against S3's Date header
Linux EC2 instance /etc/chrony.conf (Ubuntu: /etc/chrony/chrony.conf) 169.254.169.123 (IPv6: fd00:ec2::123 on Nitro-based instances) chronyc sources -v | grep -F ^* and chronyc tracking
Linux server outside AWS Same chrony file, or /etc/ntp.conf if you run ntpd pool time.aws.com iburst chronyc tracking

Linux: the steps in order

Chrony is the modern time daemon (a background program whose whole job is keeping the clock honest). NTP, the Network Time Protocol, is the language it speaks to time servers. Amazon Linux 2023 and recent Amazon Linux 2 are already configured to use the Amazon Time Sync Service, so on those you're usually verifying, not configuring.

  1. See what is syncing. Run chronyc sources -v | grep -F ^*. The line starting with ^* is the source the clock is currently following. If nothing prints, nothing is being followed.
  2. Install chrony if it's missing. On older Amazon Linux 2 it's sudo yum install chrony (after sudo yum erase 'ntp*' to remove the old daemon). On Ubuntu it's sudo apt install chrony.
  3. On EC2, add the local source. Put server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4 before any other server or pool lines in the chrony config file.
  4. Off AWS, add the public source. Add pool time.aws.com iburst, then run sudo service chronyd force-reload. On an EC2 instance, keep the local line and use the public pool only as a backup.
  5. Restart the daemon with sudo systemctl restart chronyd (on Ubuntu the service is called chrony).
  6. Check the result with chronyc tracking. The System time line says how far the clock sits from NTP time. On a healthy instance that figure is a few millionths of a second.
  7. Rerun the failing S3 command.

Two details save headaches. The 169.254.169.123 address is link-local, meaning it only works from inside your VPC (your private network on AWS), and it needs no internet access, so it works from private subnets with no NAT gateway. time.aws.com does need the internet, which is why it's the backup on EC2 and the main choice everywhere else.

Windows: the paths that work

For an ordinary Windows PC, the Control Panel route in the table above is the one to use. The Internet Time tab isn't available if the PC belongs to a domain, because a domain PC follows the domain controller's clock instead. For a Windows EC2 instance, check first: Amazon Windows AMIs released since August 2018 already use the Amazon Time Sync Service, and they check in with the time server every 900 seconds (1,024 seconds on Windows Server 2025) with the service asked to adjust every 120 seconds.

If a Windows instance's clock is wrong anyway, run w32tm /query /configuration, confirm NtpServer shows 169.254.169.123, and then force a resync with w32tm /resync /rediscover.

⚠️ What this actually breaks

Don't hand-set the clock on a Windows machine that's joined to a domain. A time skew of more than five minutes can break authentication against the domain, and you can lock people out while fixing an S3 problem. On a domain-joined machine, the clock follows the domain's time hierarchy, so change it there.

"Can't I just set the clock by hand and move on?" Jake asked.

"You can, and the error will vanish," Ethan said. "For a while. A hand-set clock is a promise that nothing will ever drift again, and clocks are terrible at keeping promises. Automatic sync is the only fix that stays fixed."

The CLI route: measure, fix, rerun, and read the request IDs

If you live in a terminal, this is the whole loop. Run it on the machine that runs the failing command.

# 1. This machine's clock, in UTC
date -u

# 2. S3's clock (the Date header on any S3 response)
curl -sI https://s3.amazonaws.com | grep -i '^date'

# 3. Which endpoint is the CLI really calling, and what did it send?
aws s3 ls --debug 2>&1 | grep -i -E 'date|x-amz-request-id|x-amz-id-2'

# 4. What is chrony following, and how far off is it?
chronyc sources -v | grep -F ^*
chronyc tracking

Step 3 does double duty. The --debug flag makes the CLI print the HTTP conversation, and that's where the two request IDs (x-amz-request-id and x-amz-id-2) show up. You'll want them if this ends in a support case. It also shows the host the CLI actually called, which answers a question people forget to ask: is this really S3?

After the fix, rerun the same command that failed. If it now succeeds, you're done. If it fails again, look at RequestTime and ServerTime in the new error. Did the gap shrink? Then you moved the right clock but not far enough. Did it stay identical? Then you changed a clock that isn't the one signing the request.

A few edge cases the quick fixes skip:

  • It works in the console but fails in the CLI. That points at the machine the CLI runs on. The bucket, the account and the permissions are clearly fine.
  • Cross-account buckets, opt-in Regions and organization-managed accounts. Nothing in the timestamp rule cares whose bucket it is. Region and account settings decide what you may do; they don't change what time it is.
  • A bare 403 from HeadObject. A HEAD request gets no response body, so when it fails S3 returns only a generic code such as 403 Forbidden. In the CLI that reads An error occurred (403) when calling the HeadObject operation: Forbidden. A missing permission produces that exact line, and so can a skewed clock, so run the date check before you rewrite a policy.
  • Temporary credentials. An expired session token produces its own error (ExpiredToken, an HTTP 400), which is a different problem with a different fix.

You fixed the clock and it still fails: the "still not working" checklist

You synced the clock, the display looks right, and the same error is back. Work through these in order, cheapest first.

  1. Read the error again. Is it really RequestTimeTooSkewed? Similar-looking errors have different causes; the table below sorts them.
  2. Compare the two times in the new error. If the gap is unchanged, you fixed a different machine.
  3. Check that the time daemon is actually running, not merely installed. chronyc tracking should answer. If it errors out, the daemon is stopped.
  4. Check where the command runs. Container, virtual machine, scheduled job on another server, CI runner. Run date -u in that exact place.
  5. Check for a jump. A restore, a resume or a source switch can throw the clock off after you last looked.
  6. Check for hand-written signing code. If nobody on your team uses an SDK or the CLI, the bug may be in the code, not the clock.
Error Status What it means Look at
RequestTimeTooSkewed 403 Request time and S3's time are too far apart The clock on the sending machine
SignatureDoesNotMatch 403 The signature S3 calculated differs from yours; check your secret access key and signing method Keys, signing code, proxies that alter headers
InvalidAccessKeyId 403 The access key ID doesn't exist in AWS's records The key itself, deleted or mistyped
ExpiredToken 400 The provided token has expired Temporary credentials that need refreshing
AccessDenied / Request has expired 403 A presigned URL was used after its expiration time Create a fresh URL; see the presigned URL section below

When you write your own signing code, a wrong timestamp can be one reason the signatures disagree, so a "signature" error can turn out to be a time problem in disguise. With the CLI or an SDK, RequestTimeTooSkewed is specific: it's about time.

Where the wrong clock hides: VMs, containers, restores and quiet servers

The opening paragraph made a claim: the clock that matters is rarely the one you're looking at. Here are the places it actually lives.

Restored and resumed machines. Big forward or backward jumps on a Windows instance have three usual causes: EC2 restored the instance from a snapshot, the Windows time service corrected a large offset, or the instance switched time sources. XenServer VM Tools (formerly Citrix VM Tools) for Windows can also interfere with time synchronization. The lesson is general: after any machine has been restored, paused or migrated, look at its clock before trusting it.

A Windows instance whose time zone was changed. By default, Windows instances use UTC. If you set a different time zone and then restart, the time becomes offset, and the instance can temporarily lose its IP address for hours. Scheduled tasks then run at the wrong moment, and S3 requests get a timestamp from the wrong hour.

Containers and other layers. I can't tell you from the outside how your particular container platform gets its time, and I won't pretend to. What I can tell you is the test: run date -u inside the container, in the same place the failing command runs, and compare it with S3's Date header. That test works whatever the layer.

Quiet servers with no time source. The old PC in the back room, set up once in 2019 and never touched since, is the technological cousin of a fridge clock blinking 12:00 forever. Left without an NTP source, it drifts. That's what pool time.aws.com iburst is for: the public Amazon Time Sync Service at time.aws.com exists for resources outside AWS.

Custom clients with two date headers. A signed S3 request can carry its date in the HTTP Date header or in x-amz-date. If both are present, x-amz-date always wins. So if your own code refreshes one and leaves a stale value in the other, S3 reads the stale one, and the failure looks like a clock problem even though the machine's clock is perfect.

Busy EC2 instances. One more case the usual advice skips. The link-local addresses on EC2 (the ones that only work inside your VPC) share a limit of 1,024 packets per second, and that budget is shared among DNS queries, instance metadata requests, Amazon Time Sync Service NTP requests and, on Windows, licensing requests. An instance that hammers DNS or the metadata service is spending the same budget its clock relies on. If a busy instance has time trouble, check that budget before you blame chrony.

"So which one is Jake's?" Ethan asked, half to himself. Row 1 of the causes table: automatic time was off on the shop PC, and nobody noticed because the display looked plausible. Plausible is the most dangerous kind of wrong.

Presigned URLs: when the wrong clock belongs to whoever made the link

A presigned URL is a link that carries its own signature, so anyone holding it can download or upload one object for a limited time without having AWS credentials of their own. Two of its query parameters matter here. X-Amz-Date is the moment the link was signed, in UTC, written like 20130721T201207Z. X-Amz-Expires is how many seconds the link stays valid; a typical value is 86400, which is 24 hours.

The important point is who wrote that timestamp. It was written by the machine that made the link, not by the customer who clicks it. So for presigned URLs, the clock you have to fix is the signer's: your web server, the backend function that builds invoice links, or the laptop where you ran aws s3 presign. The customer's clock is irrelevant.

Here's the arithmetic of what a slow signing clock does. Suppose your server's clock runs 20 minutes slow. At the true time of 10:00:00 UTC it creates a link with X-Amz-Expires=900 (15 minutes). The link is stamped 09:40:00. Counted from its own stamp, it expires at 09:55:00, which was five minutes before the customer even received it. The customer sees a dead link, and you see "it worked in testing." I can't tell you from the error text alone which timestamp error a given clock mistake will produce, so read the exact code S3 returns before deciding what you're fixing.

What you see What it means What to do
AccessDenied with Request has expired, plus Expires and ServerTime fields in the XML The link was used after its expiration time Create a new link. Compare Expires with ServerTime: a link that was already expired on arrival points at the signer's clock
RequestTimeTooSkewed on a normal signed request The sending machine's clock is more than 900 seconds from S3's Fix the clock where the request is signed
A link that dies before its stated lifetime, yet the clock looks fine It was made with temporary credentials, and it expires when those credentials do Make the link with longer-lived credentials, or shorten your expectations

Beyond the limits in the earlier table, remember that a link made with temporary credentials expires when those credentials expire, even if you asked for a later time. A link also stops working if the credentials that signed it are revoked, deleted or deactivated.

The same UTC rule applies to anyone writing their own signer. A local time of 08/01/2016 15:32:41.982-700, for example, must first be converted to UTC and submitted as 20160801T223241Z. Forget that step and your links are wrong by exactly your offset from UTC. Jake's invoice links, generated by a small script for the booking page, would be off by exactly his time zone's distance from UTC: 5 hours 30 minutes on India Standard Time, 8 hours on Singapore time. Round-number gaps like that are the signature of a forgotten conversion, not a drifting clock.

🙋‍♂️ Jake's Reality Check

"A customer said the invoice link in her text message was dead the moment it arrived. Is that the same clock thing?"

The straight answer. It can be. A link that's expired on arrival is the classic sign of a signer whose clock runs slow. But an expiry that's too short, or temporary credentials that ran out, look identical to the customer. Check the signer's clock first, then the lifetime, then the credentials.

Which SDKs fix a skewed clock on their own, and why that can mislead you

An SDK is the library your code uses to talk to AWS, and several of them deal with clock skew for you. They notice the error, work out how far off the local clock is from the server's response, and retry with a corrected timestamp. This is a kindness with a catch: it can hide the problem while it's still there.

SDK or tool Clock skew behavior What that means for you
JavaScript SDK v3 Always applies clock skew correction. The v2 setting correctClockSkew is deprecated because v3 does it unconditionally A Node app may succeed while a shell script on the same box fails
JavaScript SDK v2 Has a correctClockSkew option that applies a correction and retries Check whether it's switched on before relying on it
.NET SDK Detects a skew error, calculates the offset, retries with the right time, and stores the offset in AWSConfigs.ClockOffset. A CorrectForClockSkew property can turn it off If someone disabled it, you'll see the raw error
Java SDK v2 Ships ClockSkew and RetryUtils.isClockSkewException helpers used in its retry logic Clock skew is treated as its own class of retryable error
Java SDK v1 The default retry condition retries clock skew errors along with 500s, 503s and throttling Same caveat: correct in code, wrong on the machine
AWS CLI and Python (boto3) You see the error in real bug reports, including CreateMultipartUpload failures from aws s3 cp --recursive. A feature request for skew correction in boto3 has existed since 2017 Don't wait for a code fix; fix the clock

If one program on a machine works and another fails with this error, one possible reason is that one of them quietly corrects for the skew and the other doesn't. The working program hasn't proven the clock is fine. A bug report in the .NET SDK's GitHub repository adds a subtlety: correction that retries on every skew-type error can waste time when the real problem was something else, which is one more reason to read the code S3 returned.

Ethan's rule of thumb: "Treat SDK correction like a spare tire. It gets you home. It doesn't mean the car is fine."

What it costs: $0.00 per failed call, and how fast a drifting clock gets there

The good news first. In general, S3 bucket owners are billed for requests that succeed and for requests that fail with an HTTP 4XX client error. But the list of specific error codes that aren't billed includes RequestTimeTooSkewed, which sits in the 403 group alongside SignatureDoesNotMatch and InvalidAccessKeyId. So in US East (N. Virginia), the request charges for every failed call in this story are $0.00, however many times your script retries. That's the current list as of October 1, 2026. The Amazon Time Sync Service itself has no additional charge either.

The bad news is that "no charge" is not the same as "no cost." The cost here is the work that quietly didn't happen. Jake's script failed on three nights and nobody was told. The actual bill was one disputed $180 warranty claim, a missing photo and a very unpleasant Friday.

Reading (worked example) Value Arithmetic
S3's clock when the backup starts 02:00:00 UTC The reference
Jake's shop PC says 01:38:00 UTC 22 minutes behind = 1,320 seconds
Allowed skew 900 seconds 1,320 − 900 = 420 seconds (7 minutes) over
Earliest reading that would pass 01:45:00 UTC 02:00:00 − 15 minutes
Request charges for the failed uploads $0.00 The error code is on the not-billed list

A clock doesn't usually break in one step; it drifts. This table is hypothetical arithmetic, not a claim about any real machine. Each row is 900 seconds divided by the drift per day.

If a clock drifts by It crosses 900 seconds after What that feels like
5 seconds a day 180 days Works for six months, then stops on a random Tuesday
30 seconds a day 30 days Breaks about a month after the last reboot or sync
2 minutes a day 7.5 days (7 days 12 hours) Breaks weekly and looks intermittent
10 minutes a day 1.5 days (36 hours) Obvious within two days

The slow rows are the treacherous ones: a clock that's fine for six months and then fails looks like an AWS problem, because nothing on your side visibly changed.

Automate the check so this can't sneak up again

If you run a scheduled job that talks to S3, give it a pre-flight check: compare the machine's clock with S3's, and refuse to run (loudly) if they disagree by more than 10 minutes. That leaves five minutes of headroom under the real limit, so you find out about drift before S3 tells you.

#!/usr/bin/env bash
# Abort the backup if this machine's clock is more than 10 minutes from S3's.
s3_date=$(curl -sI https://s3.amazonaws.com | tr -d '\r' | awk -F': ' 'tolower($1)=="date" {print $2}')
s3_epoch=$(date -u -d "$s3_date" +%s)
now_epoch=$(date -u +%s)
skew=$(( now_epoch - s3_epoch ))
abs=${skew#-}

if [ "$abs" -gt 600 ]; then
  echo "Clock is ${skew}s away from S3 - not running the backup" >&2
  exit 1
fi

aws s3 sync /shop/photos s3://your-bucket/photos

The date -d form is GNU date, the one on most Linux systems; macOS's date takes different flags. A positive skew means your clock is ahead of S3's, and a negative one means it's behind. Wire the exit 1 to something that tells a human: an email, a chat message, an alarm. A silent failure was the whole problem.

A nightly log of chronyc tracking on Linux, or w32tm /query /status on Windows, leaves a drift trail. The first sign of a bad clock should be a line in a log, not a Friday afternoon.

What changed lately in AWS time sync, and why you don't need it for this

🕐 What changed between versions

  • Before: microsecond-accurate time without a placement group was limited to a handful of Regions (US East N. Virginia and Ohio, Asia Pacific Malaysia, Thailand and Tokyo, Europe Stockholm) and a few instance families.
  • Now: on June 30, 2026, Amazon Time Sync Service added microsecond-accurate time on 26 additional EC2 instance types in all commercial Regions, through a precision time placement group (a placement group created with the precision-time strategy). Precision time placement groups cost nothing extra and support newer instance families such as M7, M8, C7, C8, R7, R8, X8g and I8g.
  • What that means here: nothing. The S3 window is still 15 minutes as of October 1, 2026, and the ordinary NTP source on every EC2 instance is far more accurate than that already.

Microsecond clocks matter when you're ordering events across machines or measuring one-way network latency. Your S3 error tolerates 900 seconds, which is 900,000,000 microseconds. You don't need better hardware. You need the clock you already have to be switched on and pointed at a time source.

When nothing works: what to gather before you open a support case

If you've synced the clock, run the check in the right place, and the error still won't budge, it's time to ask AWS. You'll get further, faster, with the right evidence in the first message. Whenever you contact AWS Support about an S3 error, you must provide the request IDs for the failed action. They come in a pair and are returned in every S3 response, including error responses.

  1. The full error. The complete XML or SDK message, including RequestTime, ServerTime and MaxAllowedSkewMilliseconds.
  2. Both request IDs. x-amz-request-id and x-amz-id-2. The CLI prints them when you add --debug. SDK errors show them as the request ID and the S3 extended request ID. You can also recover them from CloudTrail data events, S3 server access logs or a browser's developer tools.
  3. The exact time of failure in UTC, plus the output of date -u taken at the same moment on the sending machine.
  4. The time-sync evidence. chronyc tracking and chronyc sources -v on Linux, or w32tm /query /status and w32tm /query /configuration on Windows.
  5. The endpoint and the tool. The host from your --debug output, the Region, the CLI or SDK and its version.
  6. Whether the same command works in CloudShell. That single line tells Support whether to look at your machine or at your account.

Now the honest part: what Support can and can't do. With the request IDs, AWS can look at what happened to a specific request on its side. What Support can't do is set the clock on your laptop, your VM or your on-premises server, and it can't help if the endpoint you're calling isn't Amazon's. If the clock on your machine is 40 minutes off and you can't or won't fix it, the answer is not a support case. It's the clock.

RequestTimeTooSkewed FAQ: the questions people actually type

What does RequestTimeTooSkewed mean in Amazon S3?

It means the timestamp on your request and S3's own clock are more than 15 minutes apart, in either direction. S3 returns it as HTTP 403 Forbidden, but it's a clock problem, not a permissions problem. The fix is on the machine that signed the request: put it on automatic time and send the request again.

How much clock skew does S3 allow before it rejects a request?

As of October 1, 2026, S3 accepts a request whose timestamp is within 15 minutes (900 seconds) of its own clock. The error's MaxAllowedSkewMilliseconds field shows it as 900000. Most other AWS services use five minutes, so don't confuse the two.

Why do I get "The difference between the request time and the current time is too large"?

That sentence is the message text of RequestTimeTooSkewed. Subtract RequestTime from ServerTime in the error XML. If the gap is over 900 seconds, the sending machine's clock is wrong, or hand-written signing code is mishandling UTC. A gap equal to your time zone's UTC offset points at the second cause.

How do I fix RequestTimeTooSkewed in the AWS CLI?

The CLI isn't the problem; the clock on the machine running it is. Compare date -u with the Date header from curl -sI https://s3.amazonaws.com. Turn on automatic time, or point chrony at time.aws.com (or 169.254.169.123 on EC2), then rerun the command. Add --debug if you need the request IDs.

Why is RequestTimeTooSkewed still happening after I synced my clock?

Usually one of three things: you fixed a different machine than the one that signs the request, the time daemon isn't actually running, or something jumped the clock afterward, such as a restore, a resume or a time-source switch. Compare RequestTime and ServerTime in the new error. An unchanged gap means you changed the wrong clock.

Can RequestTimeTooSkewed happen if my clock is ahead, not behind?

Yes. The rule is about the difference between the two clocks, so a clock 16 minutes fast fails exactly like one 16 minutes slow. In the Java SDK's convention, a positive skew means a fast client clock and a negative one a slow clock; beyond 900 seconds, either sign triggers the error.

How do I fix RequestTimeTooSkewed on an EC2 instance?

Point the instance at the Amazon Time Sync Service at 169.254.169.123. Amazon Linux 2023 and recent Amazon Linux 2 already use it; confirm with chronyc sources -v | grep -F ^*. Windows AMIs released since August 2018 use it by default; confirm with w32tm /query /configuration. You don't need a precision time placement group for this error.

How do I fix RequestTimeTooSkewed on Windows?

On a regular PC, open Control Panel, choose Date and Time, open the Internet Time tab, choose Change settings, check Synchronize with an Internet time server, and enter time.aws.com. On a domain-joined machine, fix the time through the domain instead. On a Windows EC2 instance, use the w32tm commands shown above, then w32tm /resync /rediscover.

Why does RequestTimeTooSkewed only happen inside Docker or a virtual machine?

Because the clock that signs the request lives inside that environment, and it can differ from the one on your desk. Run date -u in the same place the command runs and compare it with S3's Date header. Restores, pauses, migrations and time-source switches are known causes of big clock jumps. I can't say how your particular platform gets its time.

Is RequestTimeTooSkewed the same as SignatureDoesNotMatch?

No. Both come back as HTTP 403. RequestTimeTooSkewed means the clocks are too far apart. SignatureDoesNotMatch means the signature S3 calculated differs from yours, so check the secret access key and the signing method. Hand-written signing code can trigger either, so prefer an SDK or the CLI.

What is the difference between RequestTimeTooSkewed and "Request has expired" on a presigned URL?

"Request has expired" is an AccessDenied response for a presigned URL used after its expiration; the fix is a fresh link. RequestTimeTooSkewed means the sending clock is more than 900 seconds from S3's. They're linked because a slow clock on the machine that made the link can produce a link that is expired on arrival.

Why does RequestTimeTooSkewed show up halfway through a big upload?

S3 compares each request's timestamp with its own clock when that request arrives, and a multipart upload is many requests. If the machine's clock moved partway through the run, the later requests fail even though the early ones succeeded. Fix the clock and start the upload again.

Do failed RequestTimeTooSkewed requests cost anything?

No request charges. RequestTimeTooSkewed is on S3's list of 403 errors that aren't billed, so the failed calls cost $0.00 in US East (N. Virginia), however many times a script retries. The real cost is the job that never ran.

Does the AWS SDK correct clock skew automatically?

Some do. JavaScript SDK v3 always applies a correction, the .NET SDK computes an offset and retries, and the Java SDKs treat clock skew as a retryable error. The CLI and boto3 have surfaced the raw error in real bug reports. Correction only hides the problem; the clock is still wrong.

Why does head-object return a plain 403 Forbidden instead of RequestTimeTooSkewed?

A HEAD request gets no response body, so when it fails S3 returns only a generic code such as 400, 403 or 404, with no named error to read. A comment in the AWS SDK for .NET source says the same happens for clock skew. If the CLI prints An error occurred (403) when calling the HeadObject operation: Forbidden, run the clock check before you rewrite a bucket policy.

Can I use time.aws.com to sync a machine outside AWS?

Yes. The public Amazon Time Sync Service at time.aws.com is meant for internet-connected devices such as laptops and on-premises servers. On Linux, add pool time.aws.com iburst to your chrony config. On EC2, keep the local 169.254.169.123 line as the main source.

Jake's backup script now checks the clock before it does anything else, and Biscuit's bucket has never looked so well tended. If you've hit a case this page doesn't cover, or one of my numbers has gone stale by the time you read this, I'd rather be corrected than be quietly wrong. In the meantime, measure the gap first, fix the clock where the request is actually signed, and let automatic time do the boring work it was built for.

📌 If you keep one line from this page

S3 forgives a 15-minute gap between its clock and yours, so if you get RequestTimeTooSkewed, measure the gap on the machine that signed the request, then put that machine on automatic time.

The fix is a time source, not a permission.

Revision note. Written October 1, 2026, after one too many backups died over a few minutes of drift. Fix the clock once, add the guard, and let the log do the worrying.

Related