ApolloError response not successful (status code 400) often means a bad GraphQL request; check query, variables, and auth headers.
If you’re seeing “Response not successful: Received status code 400” in an Apollo app, you’re not alone. It feels blunt because the browser shows a number, not the real reason. The good news is that a 400 is rarely mysterious. It’s a signal that the server didn’t accept the request shape, the query document, or the variables.
This page walks you through a clean, repeatable way to find the exact cause, then fix it without guesswork. You’ll work from what you can see in your client, then confirm what the server received, then tighten the setup so it stays stable.
What A 400 Means In ApolloError
In Apollo Client, this message is raised as a network-style failure, even when the root problem is a GraphQL rule break. A 400 response means the server treated the HTTP request as invalid. That can happen before any resolver runs.
Many GraphQL servers return HTTP 200 with an errors array for runtime failures. A 400 tends to show up when parsing or validation fails, or when the JSON body can’t be read. In HTTP terms, 400 means the server can’t or won’t process the request because it sees it as a client-side problem.
| What You See | What It Often Means | First Place To Check |
|---|---|---|
| 400 with no JSON body | Request body not valid JSON, or missing query |
DevTools Network payload |
| 400 with GraphQL error text | Query fails schema validation | Operation text and variables |
| 400 only in browser, not in tools | Blocked request due to CSRF rules or cookies | Headers, cookies, origin, referrer |
| 400 after deploy | Proxy, body size limit, or header rewrite | Gateway logs and server middleware |
Client Checks For ApolloError Status Code 400
Start with the client because it’s the fastest feedback loop. Your goal is to prove three things: the endpoint is right, the request is shaped the way your server expects, and the operation you send matches the schema.
Confirm You’re Hitting The Right Endpoint
- Open DevTools Network — Filter for
/graphqland click the failing request to confirm the URL and method. - Check The Base URL — Make sure your build-time env var points at the same host you test in a manual client.
- Watch For Double Slashes — A tiny URL typo can route to a handler that returns 400 without GraphQL context.
Confirm The HTTP Headers And Body Shape
- Send JSON, Not A Raw String — The body should look like
{"query":"...","variables":{}}, not plain text. - Set Content Type — Use
Content-Type: application/jsonunless your server is set up for another media type. - Set Accept — Use
Accept: application/jsonso the server returns JSON instead of HTML.
- Disable Service Worker — Unregister it and hard refresh so it can’t rewrite the payload.
- Bypass Dev Proxy — Call the API host directly to rule out rewrites.
- Try A Fresh Window — Use a private window to rule out cached cookies and headers.
Validate Variables Before They Leave Your App
A single missing variable or a type mismatch can trigger validation failure. That turns into 400 on many servers because execution never starts.
- Log The Variables Object — Print the exact variables you pass at call time, not a stale value from a hook closure.
- Check Required Inputs — Confirm non-null inputs are present and spelled the same way as the schema.
- Match Enum Strings — Enum values are case sensitive. A one-letter mismatch can fail validation.
Run The Same Operation Outside Your App
Run the identical query and variables in a GraphQL UI such as Apollo Studio UI or GraphQL Playground. If it fails there too, your client wiring is fine and the operation is the issue.
- Copy The Query Text — Use the exact operation you ship, not a retyped version.
- Paste Variables As JSON — Keep variable names and nesting the same as your app.
- Check The Operation Name — Named operations help logs and can expose mismatched persisted queries.
ApolloError Response Not Successful – Status Code 400
When you see this error, assume the useful details are sitting in one of two places: the response body (if your server returns one) or the server logs. Your job is to pull the full request and response into view.
Get The Full Error Object In Apollo Client
Apollo Client separates GraphQL execution errors from network errors. A 400 is treated as a network error, so you want to log the network payload and any attached result.
import { onError } from "@apollo/client/link/error";
export const errorLink = onError(({
graphQLErrors,
networkError,
operation,
}) => {
if (graphQLErrors?.length) {
console.log("GraphQL errors", operation.operationName, graphQLErrors);
}
if (networkError) {
console.log("Network error", operation.operationName, networkError);
}
});
In the browser, click the failing request in DevTools and inspect the response tab. If you get JSON, it may contain a message like “Must provide query string” or a validation detail that points at the exact field.
Capture The Exact Request Payload
- Copy Request Payload — Save the
queryandvariablesfrom DevTools so you can replay them. - Check For Truncated Queries — Long strings can be cut off by a proxy or middleware limit.
- Verify Persisted Query Settings — If you use persisted queries, a hash mismatch can trigger a 400 style response.
Use this string in your notes when you’re searching logs: “apolloerror response not successful – status code 400”. It matches what your client throws and helps you connect one browser failure to one server entry.
Server Side Triggers That Lead To 400
Once you have the request payload, shift to the server. A 400 means the server rejected the request before it got into normal execution. The most common reasons fall into a handful of buckets.
Request Parsing Or Missing Query
- Confirm A Query String Exists — Many 400 responses happen when the body lacks a
queryfield. - Check JSON Parsing — Invalid JSON, trailing commas, or a wrong content type can stop parsing.
- Check Body Size Limits — Gateways may reject large payloads and return 400 before your app code runs.
Schema Validation Failures
Validation failures happen when the operation doesn’t match the schema. That includes wrong field names, wrong argument names, missing required variables, or type mismatches.
- Match Field Names Exactly — GraphQL is strict. A single typo is enough.
- Check Input Object Shape — Nested inputs must match the schema’s exact structure.
- Confirm Variable Types — A string sent where an ID is expected can fail before execution.
CSRF And Origin Rules
Some GraphQL servers block requests that look like cross-site form posts. This tends to show up in browsers, not in server-to-server calls, and it can present as a plain 400. If your request uses cookies, pay close attention to origin, headers, and the fetch credentials mode.
- Send A Preflight Header — Add a non-empty
Apollo-Require-Preflightheader when your server expects it. - Check CORS — Confirm allowed origins, allowed headers, and credential rules match your app.
- Review Cookie Settings — SameSite and Secure flags can change behavior between local and deployed.
Batched Requests And Middleware Mismatch
If a client sends a batched GraphQL request but the server does not allow it, the server can reject it with 400. A similar issue can happen when a reverse proxy rewrites the request body or strips headers that your server expects.
- Disable Batching Temporarily — Send single operations to confirm the root cause.
- Check Proxy Header Rules — Look for removed
content-typeorauthorizationheaders. - Check Compression — If the body is compressed, confirm the server can decode it.
Edge Cases In Next.js, SSR, And Proxies
Some 400 errors only show up in certain runtimes. Next.js server rendering, edge runtimes, API routes, and hosted proxies can change headers and body parsing.
Next.js Route Handlers And Body Parsing
- Confirm The Runtime — Edge runtimes can differ from Node in request streaming and headers.
- Check That Body Is Read Once — Reading a stream twice can yield an empty body and trigger “missing query”.
- Log Raw Body On The Server — In a safe dev setup, print the raw JSON string before parsing.
CDN Or WAF Blocking
Some gateways block requests based on size, header patterns, or rate controls. A blocked request may return HTML instead of JSON, which Apollo can’t parse, then it throws a network-style ApolloError.
- Inspect Response Headers — If you see a CDN header, pull logs from that layer too.
- Check For HTML Responses — An HTML error page points at a gateway rule, not a schema issue.
- Test With Curl — Send the same JSON body with
curlto compare responses.
Auth That Fails As 400
Some setups return 400 for auth problems, even when 401 or 403 would be more typical. If your server does this, you’ll need to read the response body to see the auth message.
- Confirm Token Format — Check for missing “Bearer ” prefix, whitespace, or stale tokens.
- Confirm Cookie Credentials — If you rely on cookies, set
credentials: "include"in fetch. - Check Clock Skew — JWTs can fail if server time and client time differ a lot.
Preventing 400 Errors From Returning
Once you’ve fixed the immediate issue, add a few guardrails. The goal is to catch invalid operations before they hit production, and to make the next failure readable in minutes.
Make Operations Safer At Build Time
- Use Typed Operations — Generate types from your schema so variable shapes stay aligned.
- Validate Documents In CI — Run a schema check step that fails builds on invalid queries.
- Keep One Source Of Truth — Avoid copying query strings across files where they drift apart.
Improve Logging Without Leaking Secrets
- Add A Request ID — Attach a unique id in headers so you can match client logs to server logs.
- Log Operation Name — A clear operation name makes searching logs easy.
- Redact Sensitive Fields — Strip tokens and personal data before writing logs.
Handle Network Errors Gracefully
A 400 can still happen during normal development. Don’t let it crash the entire UI. In Apollo Client hooks, catch the error and render a calm message with a retry button.
- Set An Error Policy — Choose how your UI reacts when
errorsexists. - Show The Operation Name — In dev builds, display the operation name near the error.
- Retry After A Fix — Re-run the query after you correct headers or variables.
If you’re still stuck, repeat the same loop: capture the exact payload, replay it in a GraphQL UI, then match it to one server log entry. The phrase “apolloerror response not successful – status code 400” is your breadcrumb from the browser back to the request that caused it.
If you need one extra sanity check, look at the response headers on the 400. If you see a gateway signature, test the same request against the origin server. If the origin returns a clearer JSON error, you’ve found the layer that’s masking the real message.
Once you’re back to clean responses, keep one small habit: save the last failing query and variables in a scratch file. When this pops up again, you’ll have a ready-made replay case that turns a frustrating status code into a quick fix.
Last reminder, because this catches people: a 400 is the server saying the request itself is not acceptable. Fixing it is usually about shaping the payload and headers, not rewriting your whole app.
