Errors and limits
This page lists the errors that the Management API returns and the limits that it enforces. For the End-User API, see End-User API errors and limits.
Handle errors
Failures are returned as GraphQL errors.
Inspect errors[].extensions to decide what to do.
Never parse the human-readable message, because its wording is not a stable contract and can change.
{
"data": null,
"errors": [
{
"message": "ENVIRONMENT-tier bulk jobs need filter.key.",
"extensions": {
"code": "BAD_USER_INPUT",
"reason": "FILTER_CONSTRAINT",
"field": "filter.key"
}
}
]
}
| Extension | Meaning |
|---|---|
code | The class of error. Every error has one. |
reason | A more specific machine-readable cause, present for most BAD_USER_INPUT and FORBIDDEN errors. |
field | The input field that caused the error, when one applies. |
constraint | The violated rule, for errors that distinguish several rules. |
Handle unknown reason values by falling back to the code.
New reasons can be added over time.
Authentication errors can be returned with an HTTP error status, so check the response body and not only the status.
Error codes
code | Meaning | Recovery |
|---|---|---|
UNAUTHENTICATED | The token is missing, expired, invalid, of an unsupported kind, or not valid for this environment. | Request a User Account Token or Service Account Token for the environment. |
FORBIDDEN | The token lacks a permission that an operation in the request requires. | Grant a permission from requiredPermissions, then request a new token. |
BAD_USER_INPUT | The input is invalid. See the reason values below. | Correct the input. Do not retry unchanged. |
CONCURRENT_WRITE_CONFLICT | An immediate array write could not complete because of a concurrent change. Stored data is unchanged. | Retry the request. |
BAD_REQUEST | The HTTP request is malformed, too large, or batched. | Send one valid JSON request body that is below the size limit. |
GRAPHQL_PARSE_FAILED | The GraphQL document cannot be parsed. | Fix the query syntax. |
GRAPHQL_VALIDATION_FAILED | The operation does not match the schema. | Fix the operation. |
INTERNAL_SERVER_ERROR | An unexpected failure. The message is generic and carries no internal details. | Retry later. If it persists, give the correlationId extension to Axinom support. |
For UNAUTHENTICATED and FORBIDDEN, see Authentication and permissions.
Reason codes
All reasons below are returned with the BAD_USER_INPUT code unless noted otherwise.
Request shape
reason | Cause | Extensions | Recovery |
|---|---|---|---|
OPERATION_CONSTRAINT | The operation selects more root fields than allowed. | maxRootFields | Split the operation into several requests. |
BATCHING_DISABLED | The request body is a JSON array. Returned with BAD_REQUEST. | None | Send one operation per request. |
Selecting and targeting data
reason | Cause | Extensions |
|---|---|---|
FILTER_CONSTRAINT | A bulk filter lacks a field that its tier requires, or filter IDs contradict the scope. | field, for example filter.key |
TARGET_CONSTRAINT | A write target is missing an ID that its scope needs, has an ID that its scope does not use, or has a blank ID. | field, for example target.profileId |
MATCH_CONSTRAINT | A match is empty or malformed. | field, constraint |
PATCH_CONSTRAINT | The list of pairs, or the set or remove of a pair, is invalid. | field, constraint |
MATCH_CONSTRAINT constraints:
constraint | Meaning |
|---|---|
empty | The match has no condition, or an object inside value is empty. |
notAnObject | value is not an object. |
propertyName | A property name is empty, is __proto__, contains ., or starts with $. |
PATCH_CONSTRAINT constraints:
constraint | Meaning |
|---|---|
empty | The pair list is empty, or set or remove is empty. |
pairLimit | The job has more pairs than allowed. |
notAnObject | set is not an object. |
noOperation | A pair has neither set nor remove. |
propertyLimit | set or remove has more than 50 properties. |
propertyName | A property name is empty, is __proto__, contains ., or starts with $. |
setAndRemove | A pair sets and removes the same property. |
For the rules behind these errors, see Filtering, matching, and patching.
Paging
reason | Cause | Extensions |
|---|---|---|
PAGINATION_CONSTRAINT | first is outside the allowed range. | field: first, constraint: range |
CURSOR_CONSTRAINT | The after cursor is not valid for this query. | field: after, constraint |
CURSOR_CONSTRAINT constraints are malformed, unsupportedVersion, and queryMismatch.
A cursor is valid only for the operation, environment, filter, and match that produced it.
After a CURSOR_CONSTRAINT error, restart the read without after.
Writing user data
Immediate writes use the same validation as the End-User API.
reason | Cause | Extensions |
|---|---|---|
IDENTIFIER_CONSTRAINT | A key or itemId breaks the allowed pattern or length. | identifierType, constraint |
DATETIME_CONSTRAINT | A sortDate value is not a valid date. | constraint |
EMPTY_ARRAY | replaceArrayItems was called with no items. | None |
DUPLICATE_ITEM_ID | replaceArrayItems contains the same itemId more than once. | itemId |
identifierTypeisKEYorITEM_ID.constraintispatternormaxLength.DATETIME_CONSTRAINTconstraints aretype,format,finite,integer, andrange. For accepted formats, see Datetime input format.- A JSON value above the size limit is rejected with a GraphQL error and has no dedicated
reason. Check the size of each JSON value before you send it, and do not rely on the message text.
Bulk jobs
reason | Cause | Extensions |
|---|---|---|
JOB_LIMIT | The environment already has a queued or running bulk job. | activeJobId, when the job can still be found |
INVALID_JOB_ID | The job id is not a 24-character hexadecimal string. | field: id |
JOB_NOT_FOUND | cancelBulkJob found no job with this id in the environment. | field: id |
bulkJob returns null for an id that has the correct format but belongs to no job of the environment.
When a submission fails with JOB_LIMIT, read the active job with bulkJob and activeJobId.
Wait for it to finish, or cancel it with cancelBulkJob, and then submit again.
See Monitor and cancel bulk jobs.
Validation order
The first validation failure that applies is returned.
Bulk submissions
Every input violation is rejected before a job is created.
- Required filter fields of the tier (
FILTER_CONSTRAINT). - Filter IDs against the filter scope (
FILTER_CONSTRAINT). - For patches, the pair list and the
setandremoveof each pair (PATCH_CONSTRAINT), then the pair matches (MATCH_CONSTRAINT). For deletes, the optional match (MATCH_CONSTRAINT). - The one-active-job rule (
JOB_LIMIT).
Immediate writes
- Scalar values such as
key,itemId, and sort dates (IDENTIFIER_CONSTRAINT,DATETIME_CONSTRAINT). - The target against its scope (
TARGET_CONSTRAINT). - For
replaceArrayItems, an empty item list (EMPTY_ARRAY), then repeateditemIdvalues (DUPLICATE_ITEM_ID).
Authorization
Authentication and permission checks run before any of the validation above. If an operation selects several root fields, a permission failure on any of them stops the whole operation.
Bulk job failures
Problems that appear while a job runs are not GraphQL errors.
They are recorded on the job.
Read them with bulkJob.
- A job with
status: FAILEDhas anerrorwith a machine-readablecode, amessage, and the number ofattemptsat the failing batch. Useerror.codeand do not parse the message. - A job retries transient failures, such as write conflicts, automatically before it fails. Other failures stop the job straight away.
error.codeis a machine-readable code, such as a reason or error code from this page, orBULK_JOB_FAILEDwhen no more specific code is available. Handle unknown values as a generic job failure. Submit the job again after you resolve the cause, or contact Axinom support if the failure repeats.- Records that a job could not write are reported in
results, not as an error.results.rejectedcounts records with the reasonVALUE_TOO_LARGE, andresults.skippedcounts records that changed during the job. Run the job again to pick up skipped records.
See Monitor and cancel bulk jobs for how to read progress and results.
Limits
Fixed limits
| Input or behavior | Limit |
|---|---|
| Key | ^[\w-]+(:[\w-]+)*$ |
itemId | Same pattern as a key, at most 256 characters. |
first of data reads and key listings | 1 to 500. Omitted or null defaults to 20. |
first of bulkJobs | 1 to 100. Omitted or null defaults to 20. |
Properties in one set | At most 50. |
Names in one remove | At most 50. |
| Bulk job ID | Exactly 24 hexadecimal characters. |
| Active bulk jobs | One queued or running job per environment, across all operations, tiers, keys, and submitters. Dry runs count. |
| HTTP batching | Not supported. Send one operation per request. |
A job slot is released when its job finishes. Cancelling a queued job frees the slot immediately. A running job keeps the slot until it stops, which is at its next batch boundary.
Configurable limits
These are default values. A deployment can use different values.
| Limit | Default | What happens when exceeded or reached |
|---|---|---|
| Root fields per operation | 10 | OPERATION_CONSTRAINT. Aliases and fragment spreads count, and @skip and @include do not reduce the count. |
| Request body size | 5 MB | HTTP 413 with BAD_REQUEST. |
| Pairs in one bulk patch | 10,000 | PATCH_CONSTRAINT with pairLimit. |
| Records examined per read page | 5,000 | A page with a match can be short or empty while hasNextPage is true. |
| Retention of finished real runs | 365 days | The job record is deleted. The changed data is not affected. |
| Retention of finished dry runs | 7 days | The job record is deleted. |
The documented JSON value limit is 10,240 bytes per value and is deployment-configured.
It applies to JSON inputs, including item and key-value values, match.value, and set, and to the merged value of a bulk patch.
A merged value above the configured limit is rejected as VALUE_TOO_LARGE without stopping the job.
The expiresAt field of a finished job tells you when its record becomes eligible for automatic deletion.
Cleanup is asynchronous.
Operation and request limits
An operation can select at most the configured number of root fields, 10 by default.
Aliases and fragment spreads count as separate selections, even when the field names are identical.
An operation over the limit is rejected before it runs, and the maxRootFields extension reports the limit in effect.
To read or write more, send several requests.
Each HTTP request carries one JSON body, and the body size is limited. Large bulk patches count against this limit as well as the pair limit.
Recovery for common limit errors
| Situation | What to do |
|---|---|
OPERATION_CONSTRAINT | Send fewer root fields per request. Do not use aliases to repeat a field many times. |
PATCH_CONSTRAINT with pairLimit | Split the patch into several jobs. Submit the next job after the previous one finishes. |
JOB_LIMIT | Wait for activeJobId to finish, or cancel it, then submit again. |
HTTP 413 | Send a smaller body. For a large patch, split it into several jobs. |
A short or empty page with hasNextPage: true | Keep following endCursor. The read is not finished until hasNextPage is false. |
VALUE_TOO_LARGE in job results | Submit a corrected patch that keeps values under the limit, for example by removing properties, and run it again. |
Progress is read-only
Playback progress entries can be read with listKeyValues, but no Management API operation changes them.
setKeyValue never writes a progress entry, even when one is stored under the same key.
Bulk key-value jobs select JSON entries only.
A progress value reflects what has been stored, so it can trail live playback slightly.