floci-io/floci · error · AwsException

MissingAction

MissingAction

Error message

The request must contain the parameter Action

What it means

Thrown by the AWS query-protocol integration path (invokeQuery) when the form-encoded body produced by an integration's VTL template has no 'Action' parameter, or its value is blank. In the AWS query protocol (used by SQS-style APIs), 'Action' selects the operation, mirroring AwsQueryController; without it Floci cannot pick an operation and returns MissingAction with HTTP 400 before dispatching to any handler.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/apigateway/AwsServiceRouter.java:198

    /**
     * Dispatches an AWS query-protocol (form-encoded) integration request.
     *
     * <p>Used for {@code path/}-style integration URIs whose VTL request template renders an
     * {@code application/x-www-form-urlencoded} body in the AWS query protocol, e.g.
     * {@code Action=SendMessage&QueueUrl=...&MessageBody=...}. The {@code Action} parameter
     * selects the operation, mirroring {@link io.github.hectorvent.floci.core.common.AwsQueryController}.
     *
     * @param service the AWS service name from the URI (e.g., "sqs")
     * @param params  the parsed form parameters, including {@code Action}
     * @param region  the AWS region
     * @return the service response (query-protocol XML)
     */
    public Response invokeQuery(String service, MultivaluedMap<String, String> params, String region) {
        String action = params.getFirst("Action");
        LOG.debugv("AWS query integration dispatch: {0}:{1} in {2}", service, action, region);

        if (action == null || action.isBlank()) {
            throw new AwsException("MissingAction",
                    "The request must contain the parameter Action", 400);
        }

        try {
            return switch (service) {
                case "sqs" -> sqsQueryHandler.handle(action, params, region);
                default -> throw new AwsException("UnknownService",
                        "Unsupported AWS query-protocol service integration: " + service, 400);
            };
        } catch (AwsException e) {
            throw e;
        } catch (Exception e) {
            throw new AwsException("InternalError",
                    e.getMessage() != null ? e.getMessage() : "Service invocation failed", 500);
        }
    }
}

View on GitHub (pinned to 62ff490619)

Solutions

  1. Make the VTL request template always emit a literal Action, e.g. 'Action=SendMessage&QueueUrl=$input.path('$.queueUrl')&MessageBody=$util.urlEncode($input.body)'.
  2. Ensure Content-Type handling matches: the integration must parse the body as application/x-www-form-urlencoded, not JSON.
  3. Use test-invoke on the method and inspect the rendered request to confirm 'Action' is present and non-empty.
  4. Verify the integration URI style: this code path only serves path/-style query integrations (currently SQS-only); JSON-body integrations go through invokeJson() instead.

Example fix

# before (request template)
QueueUrl=$input.path('$.queueUrl')&MessageBody=$util.urlEncode($input.body)

# after
Action=SendMessage&QueueUrl=$input.path('$.queueUrl')&MessageBody=$util.urlEncode($input.body)
Defensive patterns

Strategy: validation

Validate before calling

# Validate the rendered template locally before deploy
RENDERED=$(vtl-render request.vtl sample-input.json)
if ! echo "$RENDERED" | grep -qE '(^|&)Action=[^&]+'; then
  echo "Template must render a non-empty Action parameter" >&2; exit 1
fi

Try / catch

catch (AwsException e) {
    if ("MissingAction".equals(e.getCode())) {
        // template bug: add Action=<Op> to the form body; no retry will help
        throw new TemplateConfigurationException("Query-protocol template must emit Action=...", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An API Gateway method with type AWS whose URI is 'path/'-style and whose request template renders application/x-www-form-urlencoded content that omits Action=, e.g. only 'QueueUrl=...&MessageBody=...'. Also triggered when the VTL template sets Action from a variable that is null or empty at runtime.

Common situations: Copying a JSON-integration template into a query-protocol integration. Template variables like $input.params('Action') that are absent on the actual request, so Action renders blank. POST bodies sent as JSON while the integration expects form-encoding, so the form parse yields no parameters.

Related errors


AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14). Data as JSON: /api/errors/c8815da3aba9773c. Report an issue: GitHub.