aws-sdk-js-v3 icon indicating copy to clipboard operation
aws-sdk-js-v3 copied to clipboard

InvalidSpotFleetRequestConfig: Parameter: SpotFleetRequestConfig.ValidUntil is invalid

Open mifi opened this issue 7 months ago • 2 comments

Checkboxes for prior research

Describe the bug

Regression where any Date sent in ValidFrom/ValidUntil causes an error to be thrown. The crash is reported by the server and seems to be related to the milliseconds.

Regression Issue

  • [x] Select this option if this issue appears to be a regression.

SDK version number

@aws-sdk/client-ec2@npm:3.817.0

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

18.20.4

Reproduction Steps

Note that I've redacted some things.

yarn add @aws-sdk/client-ec2
// test.js
import { EC2Client, RequestSpotFleetCommand } from '@aws-sdk/client-ec2';

const client = new EC2Client({ region: 'us-east-1' });
const dryRun = false;
await client.send(new RequestSpotFleetCommand({
  DryRun: dryRun,
  SpotFleetRequestConfig: {
    IamFleetRole: 'arn:aws:iam::REDACTED',
    AllocationStrategy: 'capacityOptimized',
    TargetCapacity: 1,
    ValidUntil: new Date(Date.now() + (1000 * 60 * 60)),
    TerminateInstancesWithExpiration: true,
    Type: 'maintain',
    TargetCapacityUnitType: 'units',
    LaunchSpecifications: [
      {
        ImageId: 'ami-REDACTED',
        BlockDeviceMappings: [
          {
            DeviceName: '/dev/sda1',
            Ebs: {
              DeleteOnTermination: true,
              VolumeType: 'gp2',
              VolumeSize: 8,
              SnapshotId: 'snap-REDACTED',
            },
          },
        ],
        IamInstanceProfile: {
          Arn: 'arn:aws:iam::REDACTED',
        },
        KeyName: 'REDACTED',
        SecurityGroups: [
          {
            GroupId: 'sg-REDACTED',
          },
        ],
        UserData: 'REDACTED',
        InstanceRequirements: {
          VCpuCount: {
            Min: 8,
            Max: 8,
          },
          MemoryMiB: {
            Min: 4096,
          },
          SpotMaxPricePercentageOverLowestPrice: 100,
        },
      },
    ],
  },
}));
node test.js

Note the ValidUntil prop.

Observed Behavior

node_modules/@smithy/smithy-client/dist-cjs/index.js:379
  const response = new exceptionCtor({
                   ^

InvalidSpotFleetRequestConfig: Parameter: SpotFleetRequestConfig.ValidUntil is invalid.
    at throwDefaultError (node_modules/@smithy/smithy-client/dist-cjs/index.js:379:20)
    at node_modules/@smithy/smithy-client/dist-cjs/index.js:388:5
    at de_CommandError (node_modules/@aws-sdk/client-ec2/dist-cjs/index.js:17188:10)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async node_modules/@smithy/middleware-serde/dist-cjs/index.js:36:20
    at async node_modules/@smithy/core/dist-cjs/index.js:193:18
    at async node_modules/@smithy/middleware-retry/dist-cjs/index.js:320:38
    at async node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:33:22
    at <anonymous> (test.ts:5:18) {
  '$fault': 'client',
  '$metadata': {
    httpStatusCode: 400,
    requestId: '0856c9e6-159e-482c-8c2b-ee3fa2965def',
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  Code: 'InvalidSpotFleetRequestConfig'
}

Expected Behavior

It should start the fleet request.

Possible Solution

Must support Date in ValidFrom/ValidUntil (as the typescript definitions suggests).

Additional Information/Context

Note that:

  • If I set dryRun to true, it doesn't error.
  • If I downgrade to @aws-sdk/client-ec2 3.427.0 it works.
  • If I change ValidUntil to { toISOString: () => new Date(Date.now() + (1000 * 60 * 60)).toISOString().replace(/\.\d+Z$/, 'Z') } it works.

This suggests that the @aws-sdk/client-ec2 client calls Date.toISOString() on ValidFrom/ValidUntil and then sends to the server what is returned from those (e.g. 2025-05-26T21:37:50.051Z), however the server expects 2025-05-26T21:37:50Z (no milliseconds).

How am I the only person who seems to have hit this obvious bug in the JS SDK?

Related: https://github.com/boto/boto3/issues/714

mifi avatar May 26 '25 21:05 mifi

Hey @mifi ,

Thanks for your feedback! I can reproduce this issue. I think the root cause is that - the service model accepts UTC format timestamp (for example - 2025-05-27T21:09:50Z ) for ValidUntil/ ValidFrom, but the SDKis sending Tue May 27 2025 14:19:32 GMT-0700 with new Date(Date.now() + (1000 * 60 * 60)) .

As you can see on JS SDK model file , the ValidFrom and ValidUntil are either Date type of undefined .

 ValidFrom?: Date | undefined;
 ValidUntil?: Date | undefined;

I will check with service team on this to identify if it's a designed behavior and there's a doc gap, or, if there's something wrong in service model.


Workaround

You can either use the workaround you have as a workaround, or convert the Date object to UTC format -

          ValidUntil: new Date(
            Date.UTC(
              new Date().getUTCFullYear(),
              new Date().getUTCMonth(),
              new Date().getUTCDate()+1,
              new Date().getUTCHours() 
            )
          ),

zshzbh avatar May 27 '25 21:05 zshzbh

@mifi does it succeed if you use the nearest second as the timestamp?

ValidUntil: new Date((Date.now() / 1000 | 0) * 1000 + (1000 * 60 * 60))

Our specification says that milliseconds are optional in the "date-time" format that this service is supposed to be using, but it may be that the server is rejecting milliseconds.

I don't think there is a bug in the client side SDK here, since we are adhering to the specification. Maybe the service error message needs to be clearer as to why it has rejected the milliseconds.

kuhe avatar Jun 02 '25 17:06 kuhe

Got updates from service team - You used a datetime format of Tue May 27 2025 14:19:32 GMT-0700 as the value for SpotFleetRequestConfig.ValidUntil and received an error. This is expected since our public api doc and the sdk doc linked both mention that the value for ValidUntil should be in UTC format YYYY-MM-DDTHH:MM:SSZ . We parse the inputted value in this format so other DateTime formats won't work.

Rounding to the nearest second workaround is working as service api doesn't receive milliseconds. I will bring it to my team. You can use @kuhe's proposed solution as a workaround.

Thanks.

zshzbh avatar Jun 27 '25 18:06 zshzbh

This issue has not received a response in 1 week. If you still think there is a problem, please leave a comment to avoid the issue from automatically closing.

github-actions[bot] avatar Jul 08 '25 00:07 github-actions[bot]

Shouldn’t this behavior be documented? It’s very unexpected and confusing behavior. I think the JS SDK should remove the milliseconds part from the timestamp before sending it to your API

mifi avatar Jul 09 '25 13:07 mifi

As mentioned in comment above, it's documented both Service API docs and SDK API reference doc that ValidUntil should be in UTC format and SDK client side adheres to smithy specification.

The end date and time of the request, in UTC format (YYYY-MM-DDTHH:MM:SSZ). After the end date and time, no new Spot Instance requests are placed or able to fulfill the request. If no value is specified, the Spot Fleet request remains until you cancel it.

Since there's no further action item for SDK, we're going to close the issue.

aBurmeseDev avatar Aug 06 '25 23:08 aBurmeseDev

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.

github-actions[bot] avatar Aug 06 '25 23:08 github-actions[bot]

In the SDK API reference doc it's just says that:

ValidUntil Date | undefined The end date and time of the request, in UTC format (YYYY-MM-DDTHH:MM:SSZ). After the end date and time, no new Spot Instance requests are placed or able to fulfill the request. If no value is specified, the Spot Fleet request remains until you cancel it.

Also the typescript types only allow a Date object or undefined to be passed. However the docs say nothing about the need to round the Date object to nearest second. Shouldn't it? If normal date objects are not accepted, shouldn't the API instead take a string instead of Date objects?

mifi avatar Aug 07 '25 11:08 mifi

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.

github-actions[bot] avatar Aug 22 '25 00:08 github-actions[bot]