Skip to content

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

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

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions