Skip to content

SSDK returns UnknownOperationException when query parameter marked with @required and @httpQuery is not provided #1840

@Miles123K

Description

@Miles123K

Description

When a Smithy operation has a @required @httpQuery parameter, the generated SSDK throws UnknownOperationException instead of ValidationException when the parameter is missing from the request.

Expected Behavior

A request missing a required query parameter should return a ValidationException with details about the missing field.

Actual Behavior

The request fails to match any operation in HttpBindingMux, resulting in UnknownOperationException with message: "request did not match the expected service operation".

Root Cause

The HttpBindingMux uses required query parameters as part of operation routing/matching, not just validation. In UriSpec.match():

for (const querySegment of this.querySegments) {
    if (!(querySegment.key in req.query)) {
        return false;  // Routing fails here
    }
}

This means the mux cannot distinguish between:

  1. A genuinely unknown operation
  2. A known operation with missing required query parameters

Deeper Issue: Query Params Used for Routing Instead of Validation

The SSDK generates both:

  1. Per-operation handlers (e.g., GetResourceHandler) - one Lambda per operation
  2. Monolithic service handler (MyServiceHandler) - single Lambda for all operations

In both cases, the mux includes required query params for routing:

// Monolithic handler mux - routes between operations
const mux = new HttpBindingMux([
    new UriSpec('GET', 
        [{ type: 'path_literal', value: "item" }],
        [{ type: 'query', key: "id" }],  // Used for routing!
        { operation: "GetItem" }
    ),
    new UriSpec('PUT',
        [{ type: 'path_literal', value: "item" }],
        [],
        { operation: "UpdateItem" }
    ),
]);

The mux legitimately needs to distinguish operations on the same path with different methods. But using required query params for this distinction is wrong - they should be validated after routing, not used for routing.

Meanwhile, the SSDK does generate proper validation for required query params:

case "id": {
    memberValidators["id"] = new CompositeValidator([
        new RequiredValidator(),
    ]);
    break;
}

But this validation runs after the mux, so it never executes when the query param is missing.

Reproduction

Smithy model:

@http(method: "GET", uri: "/resource")
@readonly
operation GetResource {
    input: GetResourceInput
}

structure GetResourceInput {
    @required
    @httpQuery("id")
    id: String
}

Request:

GET /resource

Expected response:

{
  "__type": "ValidationException",
  ...
}

Actual response:

{
  "__type": "UnknownOperationException", 
  ...
}

Suggested Fix

The mux should only use path and method for routing. Required query params should be handled by validation, not routing. Options:

  1. Don't include @required @httpQuery params in mux querySegments - only use query params for routing when they disambiguate operations (e.g., @httpQueryParams with literal values)
  2. Two-pass matching - match on path/method first, then return partial match info for missing query params so the handler can throw ValidationException

Environment

  • @aws-smithy/server-common
  • TypeScript SSDK

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions