Skip to main content

Errors and limits

This docs site is powered by GraphQL: every page you are reading is a Doc you can query through a live API. That API is read-only and open — no API key, no Authorization header, no mutations. So the error surface is small and entirely about request shape and complexity.

Errors come back in the standard GraphQL envelope, and most carry a stable machine code at errors[].extensions.code. Branch on that code — not the human message — and your agent can recover from a malformed query without regex-parsing prose.

The error envelope

A successful request returns data; a failed one returns an errors array. Each entry has a message, usually a locations array, and — for parse and validation failures — an extensions.code:

{
"errors": [
{
"message": "Cannot query field \"nope\" on type \"ApiMeta\". Did you mean \"name\"?",
"locations": [{ "line": 1, "column": 10 }],
"extensions": { "code": "GRAPHQL_VALIDATION_FAILED" }
}
]
}

That response is exactly what the live server returns for:

{ meta { nope } }
Read the code, not the message

Human message text can change. The extensions.code values are the stable contract — pin your branching logic to them.

Field suggestions are enabled (blockFieldSuggestion: false in the server config), so a misspelled field comes back with a Did you mean …? hint that helps an agent self-correct.

Error-code reference

Because the endpoint is read-only and open, there are no auth, scope, or write-conflict codes — only request-shape and complexity failures.

extensions.codeWhen it happensSuggested agent action
GRAPHQL_PARSE_FAILEDThe query string is not valid GraphQL — a syntax error, or it tripped the token limit (see below).Fix the syntax / shrink the query. Do not retry the same string unchanged.
GRAPHQL_VALIDATION_FAILEDThe query parses but references something the schema doesn't have — an unknown field, wrong argument, or bad type.Correct the selection set against the Schema overview. The message often names the right field.
BAD_REQUESTThe HTTP body is malformed for GraphQL — most commonly an array body (request batching is disabled).Send a single, well-formed JSON operation: {"query":"…","variables":{…}}.

A GRAPHQL_PARSE_FAILED looks like this — note the <EOF> from an unterminated selection set:

{
"errors": [
{
"message": "Syntax Error: Expected Name, found <EOF>.",
"locations": [{ "line": 1, "column": 15 }],
"extensions": { "code": "GRAPHQL_PARSE_FAILED" }
}
]
}

Some guardrails are enforced before a code is attached and so arrive as a plain Syntax Error message with no extensions.code — notably the alias limit. Match on the message text for those, or just treat any errors payload without data as "my query was rejected, shrink and retry."

This endpoint is read-only

There are no Authorization headers, no API keys, and no write operations — so there are no auth or mutation errors to handle. The schema has no mutation type at all, so a mutation is rejected flatly:

{
"errors": [
{ "message": "Schema is not configured to execute mutation operation.",
"locations": [{ "line": 1, "column": 1 }] }
],
"data": null
}

Point your agent at the five Query fields (docs, doc, sections, search, meta) documented in Queries.

Transport-level rejections (HTTP status)

A request can fail before it reaches the GraphQL layer:

  • Non-JSON content type → HTTP 415 Unsupported Media Type. Always send content-type: application/json on POST.
  • Both POST and GET are accepted for these reads. A GET with a ?query=… parameter works:
curl -sS 'http://localhost:4000/graphql?query=%7Bmeta%7Bname%7D%7D' \
-H 'accept: application/json'
# {"data":{"meta":{"name":"Keen Docs GraphQL API"}}}

Limits

The server wraps GraphQL Yoga with EnvelopArmorPlugin from @escape.tech/graphql-armor. These are the configured values in graphql-server/, stated exactly. Each is a complexity bound checked before execution, so an oversized query is rejected without ever touching a resolver.

GuardrailLimitWhat it caps
maxDepth10Maximum selection-set nesting depth.
maxAliases20Maximum number of field aliases in one operation.
maxTokens1500Maximum lexical tokens in the query document.
costLimit8000Overall query cost budget (depth- and breadth-weighted).
blockFieldSuggestionfalseField "Did you mean …?" hints stay enabled.

These bounds are generous for honest queries. The schema is deliberately shallow — the deepest natural path is sections → docs → headings → anchor (four levels) — so a real selection set lands far under depth 10 and cost 8000. The limits exist to stop abusive or accidental fan-out, not to constrain normal use.

Token limit (maxTokens: 1500)

A query document with more than 1500 lexical tokens is rejected at parse time:

{
"errors": [
{ "message": "Syntax Error: Token limit of 1500 exceeded.",
"extensions": { "code": "GRAPHQL_PARSE_FAILED" } }
]
}

Alias limit (maxAliases: 20)

More than 20 aliases in a single operation is rejected. This one arrives as a plain syntax error with no extensions.code. Sending 25 aliased copies of meta:

{
"errors": [
{ "message": "Syntax Error: Aliases limit of 20 exceeded, found 25." }
]
}

Batching is disabled

Send one operation per request. An array body is rejected:

{
"errors": [
{ "message": "Batching is not supported.",
"extensions": { "code": "BAD_REQUEST" } }
]
}

Practical guidance for agents

  • Ask for exactly the fields you need. Precise selection sets stay far under the cost and token bounds and return a predictable shape.
  • Split, don't nest. If you need many things, send several small requests rather than one deep, alias-heavy query.
  • Treat a missing data key as "reshape and retry." On any errors payload, read errors[0].extensions.code when present, fix the query, and resend — never loop on the same rejected string.

Introspection is on

Introspection is enabled and GraphiQL is served at http://localhost:4000/graphql. Your agent can discover the full type system live with a standard introspection query, or just read the Schema overview. The landing page is off — the endpoint is an API surface (and an explorer), not a marketing page.

A clean request, for contrast

When the query is well-formed and within bounds, you get back exactly the shape you asked for — no envelope surprises:

curl -sS http://localhost:4000/graphql \
-H 'content-type: application/json' \
--data '{"query":"{ search(query: \"idempotent\") { id title slug } }"}'
{
"data": {
"search": [
{ "id": "graphql/errors-and-limits", "title": "Errors and limits", "slug": "/docs/graphql/errors-and-limits" },
{ "id": "graphql/queries", "title": "Queries", "slug": "/docs/graphql/queries" }
]
}
}

See also

  • Queries — the five read fields, arguments, and sample responses.
  • Schema overview — the full type system you are validating against.