floci-io/floci · error · AwsException

BadRequestException

BadRequestException

Error message

e.getMessage()

What it means

PutMethodResponse in the API Gateway emulator parses the raw request body with Jackson; any IOException from objectMapper.readValue is rethrown as a 400 BadRequestException carrying Jackson's message. Malformed JSON, a non-object payload (array/string), or unreadable content all land here.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/apigateway/ApiGatewayController.java:105

        return Response.ok(toMethodResponseNode(service.getMethodResponse(region, apiId, resourceId, httpMethod, statusCode)).toString()).type(MediaType.APPLICATION_JSON).build();
    }

    @PUT
    @Path("/restapis/{apiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}")
    public Response putMethodResponse(@Context HttpHeaders headers,
                                      @PathParam("apiId") String apiId,
                                      @PathParam("resourceId") String resourceId,
                                      @PathParam("httpMethod") String httpMethod,
                                      @PathParam("statusCode") String statusCode,
                                      String body) {
        String region = regionResolver.resolveRegion(headers);
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> request = objectMapper.readValue(body, Map.class);
            MethodResponse resp = service.putMethodResponse(region, apiId, resourceId, httpMethod, statusCode, request);
            return Response.status(201).entity(toMethodResponseNode(resp).toString()).type(MediaType.APPLICATION_JSON).build();
        } catch (IOException e) {
            throw new AwsException("BadRequestException", e.getMessage(), 400);
        }
    }

    @GET
    @Path("/restapis/{apiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}")
    public Response getIntegrationResponse(@Context HttpHeaders headers,
                                           @PathParam("apiId") String apiId,
                                           @PathParam("resourceId") String resourceId,
                                           @PathParam("httpMethod") String httpMethod,
                                           @PathParam("statusCode") String statusCode) {
        String region = regionResolver.resolveRegion(headers);
        return Response.ok(toIntegrationResponseNode(service.getIntegrationResponse(region, apiId, resourceId, httpMethod, statusCode)).toString()).type(MediaType.APPLICATION_JSON).build();
    }

    @PUT
    @Path("/restapis/{apiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}")
    public Response putIntegrationResponse(@Context HttpHeaders headers,
                                           @PathParam("apiId") String apiId,

View on GitHub (pinned to 62ff490619)

Solutions

  1. Validate the body is a JSON object before sending (jq . <<< body or a client-side parse)
  2. Send exactly the expected shape: {"selectionPattern": "...", "responseTemplates": {...}}

Example fix

// before
String body = "[\"selectionPattern\"]"; // JSON array
// after
String body = "{\"selectionPattern\": \"2\\d{2}\"}";
Defensive patterns

Strategy: validation

Validate before calling

ObjectMapper om = new ObjectMapper();
JsonNode n = om.readTree(body);
if (!n.isObject()) throw new IllegalArgumentException("Body must be a JSON object");

Type guard

boolean isValidMethodResponseBody(String body, ObjectMapper om) throws Exception {
    JsonNode n = om.readTree(body);
    return n.isObject();
}

Try / catch

try {
    apigw.putMethodResponse(req);
} catch (BadRequestException e) {
    // Jackson message indicates the exact offset; re-check the serialized body
    log.error("Rejected body: {}", rawBody);
    throw e;
}

Prevention

When it happens

Trigger: PUT /restapis/{apiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode} with a body that is not a valid JSON object — trailing commas, single quotes, empty string, or a JSON array.

Common situations: Hand-rolled curl requests with shell-quoting mistakes; SDK clients that send an empty entity on error paths; proxies or transforms that mangle the JSON body in transit.

Understand the failure class

Background: BadRequestException (HTTP 400) — NestJS 'Bad Request' Errors: Why They Fire and How to Fix Them — this error's family across 4 libraries.

Related errors


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