GraphQL
Errors
HTTP 401 is missing auth. HTTP 422 is a GraphQL error. Mutation payloads also return errors: [String!]! for domain failures.
Constructable uses two error channels: HTTP / GraphQL execution errors, and per-mutation errors arrays.
HTTP status
| Status | Meaning |
|---|---|
200 | GraphQL executed without execution errors. Still read data and any mutation errors arrays. |
401 | Missing or invalid Bearer token. GraphQL also sends WWW-Authenticate with scope="api" and the GraphQL resource metadata URL. |
422 | GraphQL returned an errors array (validation, missing record, authorization, invalid document). |
The JSON body for a GraphQL response is { "data": ..., "errors": ... }. errors is omitted on a clean 200.
Execution errors
These appear in the top-level errors array. Typical messages:
Organization not found—organization(id:)on a mutation does not resolve for this userProject not found— nestedproject(id:)on a mutation does not resolve- Permission failures from
Permit("You do not have permission…") - Unknown field or argument (the document does not match the schema)
Example:
{
"data": { "organization": null },
"errors": [
{
"message": "Project not found",
"locations": [{ "line": 4, "column": 5 }],
"path": ["organization", "project"]
}
]
}
Treat any top-level errors entry as a failed request, even if data is partially present.
Mutation payload errors
Writes return a payload with errors: [String!]!. Domain problems (validation, conflict, empty required field) usually land here instead of the top-level array, and HTTP stays 200.
mutation OpenRfi($organizationId: ID!, $projectId: ID!, $input: OpenRfiInput!) {
organization(id: $organizationId) {
project(id: $projectId) {
openRfi(input: $input) {
rfi {
id
status
number
}
errors
}
}
}
}
Handle it as:
- If the HTTP status is
401or422, inspect top-levelerrors. - If
data.organization.project.openRfiisnull, the nested mutation did not run. - If
errorson the payload is non-empty, the write did not apply. The returnedrfi(or other record) may benull. - If
errorsis[]and the record is present, the write succeeded.
Idempotent creates
Many create mutations no-op when a record with the given id already exists, returning the existing record or a null record with empty errors rather than raising. If you retry, use the same client-generated id.
Partial data
GraphQL can return data alongside errors when a sibling field fails. Check both. Do not assume data.organization is complete if errors is present.