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
- Make the VTL request template always emit a literal Action, e.g. 'Action=SendMessage&QueueUrl=$input.path('$.queueUrl')&MessageBody=$util.urlEncode($input.body)'.
- Ensure Content-Type handling matches: the integration must parse the body as application/x-www-form-urlencoded, not JSON.
- Use test-invoke on the method and inspect the rendered request to confirm 'Action' is present and non-empty.
- 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
- Start every query-protocol template with a literal Action=<Operation>.
- Keep request templates for JSON and query integrations in separate, clearly named files.
- Use test-invoke in CI to assert the rendered request contains 'Action='.
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
- BadRequestException
- TLS enabled but no certificate provided and self-signed gene
- ValidationException
- ValidationException
- floci.storage.efs root-permissions must be 3-4 octal digits
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/c8815da3aba9773c.
Report an issue: GitHub.