floci-io/floci · error · AppSyncTransportException

MalformedHttpRequestException

MalformedHttpRequestException

Error message

Request body is empty.

What it means

The GraphQL-over-HTTP execution endpoint (POST /graphql/{apiId}) received a request whose body was null or blank (empty, or only whitespace). Floci's AppSyncExecutionController.parseBody rejects this before any JSON or GraphQL parsing with HTTP 400 and error type MalformedHttpRequestException, matching real AppSync behavior for empty request bodies.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/appsync/graphql/AppSyncExecutionController.java:115

        }
    }

    private boolean isAcceptedContentType(HttpHeaders headers) {
        String contentType = headers.getHeaderString(HttpHeaders.CONTENT_TYPE);
        if (contentType == null || contentType.isBlank()) {
            return false;
        }
        String normalized = contentType.toLowerCase(Locale.ROOT).trim();
        int semicolon = normalized.indexOf(';');
        if (semicolon >= 0) {
            normalized = normalized.substring(0, semicolon).trim();
        }
        return "application/json".equals(normalized) || "application/graphql".equals(normalized);
    }

    private ParsedRequest parseBody(String body) {
        if (body == null || body.isBlank()) {
            throw new AppSyncTransportException(400, "MalformedHttpRequestException",
                    AppSyncErrorFormatter.MSG_EMPTY_BODY);
        }

        JsonNode root;
        try {
            root = objectMapper.readTree(body);
        } catch (JsonProcessingException e) {
            throw new AppSyncTransportException(400, "MalformedHttpRequestException",
                    AppSyncErrorFormatter.MSG_UNABLE_TO_PARSE);
        }

        if (root == null || root.isNull() || root.isArray() || !root.isObject()) {
            throw new AppSyncTransportException(400, "MalformedHttpRequestException",
                    AppSyncErrorFormatter.MSG_UNABLE_TO_PARSE);
        }
        if (root.isEmpty()) {
            throw new AppSyncTransportException(400, "MalformedHttpRequestException",
                    AppSyncErrorFormatter.MSG_UNABLE_TO_PARSE);

View on GitHub (pinned to 62ff490619)

Solutions

  1. Ensure the POST body is a non-empty JSON object like {"query": "{ __typename }"} and that your HTTP client actually sends it.
  2. Check intermediate proxies (dev proxy, nginx, API gateway) are not dropping or buffering the request body.
  3. Add a client-side assertion that the serialized body is non-blank before dispatching.

Example fix

// before
fetch(url, { method: 'POST', headers }); // no body sent -> 400

// after
fetch(url, { method: 'POST', headers, body: JSON.stringify({ query: '{ __typename }' }) });
Defensive patterns

Strategy: validation

Validate before calling

function assertBody(body) {
  if (body == null || body.trim() === '') throw new Error('GraphQL request body is empty');
  return body;
}
fetch(url, { method: 'POST', body: assertBody(JSON.stringify({ query })) });

Try / catch

const resp = await fetch(url, req);
if (resp.status === 400) {
  const err = await resp.json();
  if (/empty/i.test(err.errors?.[0]?.message ?? '')) throw new Error('bug: request body not sent — check client/proxy');
}

Prevention

When it happens

Trigger: POSTing to the GraphQL endpoint with Content-Length 0, a body of only spaces/newlines, or letting an HTTP client send no body at all (e.g. forgetting to attach the JSON payload, or a GET-style invocation of a POST endpoint). Sending an empty string as the body field also triggers it.

Common situations: Missing .body(...) in fetch/axios calls; proxy or load balancer stripping request bodies; curl invocations where the --data flag was omitted; SDK code paths that build the request conditionally and skip the payload.

Related errors


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