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

StatusMeaning
200GraphQL executed without execution errors. Still read data and any mutation errors arrays.
401Missing or invalid Bearer token. GraphQL also sends WWW-Authenticate with scope="api" and the GraphQL resource metadata URL.
422GraphQL 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 foundorganization(id:) on a mutation does not resolve for this user
  • Project not found — nested project(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:

  1. If the HTTP status is 401 or 422, inspect top-level errors.
  2. If data.organization.project.openRfi is null, the nested mutation did not run.
  3. If errors on the payload is non-empty, the write did not apply. The returned rfi (or other record) may be null.
  4. If errors is [] 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.