Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Error Handling: Correctly Using GraphQlInputException and GraphQlAuthorizationException

Error Handling: Correctly Using GraphQlInputException and GraphQlAuthorizationException

~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

The previous chapters already used GraphQlNoSuchEntityException (chapter 15) and GraphQlAuthorizationException (chapter 18) without systematically introducing the full exception family. This chapter wraps up block 5 with exactly that overview, and adds the GraphQlInputException that's been missing so far.

Why not just throw a plain exception?

A regular \Exception or \RuntimeException would also get caught by Magento, but treated as a generic, uncategorized internal server error - in production mode with the unhelpful message "Internal server error" and no hint at all about what went wrong. The GraphQL-specific exceptions from the \Magento\Framework\GraphQl\Exception namespace, on the other hand, each carry their own category, which ends up in the response field extensions.category - clients can then distinguish between error kinds precisely, instead of treating every error the same way.

The four most important GraphQL exceptions

  • GraphQlInputException - category graphql-input: business-invalid input that's syntactically valid GraphQL (e.g. a capacity of -5).
  • GraphQlAuthorizationException - category graphql-authorization: the request is syntactically/logically fine, but the caller isn't allowed to run it (chapter 18).
  • GraphQlNoSuchEntityException - category graphql-no-such-entity: the referenced entity doesn't exist (chapter 15).
  • GraphQlAlreadyExistsException - category graphql-already-exists: a creation operation would duplicate an already-existing, unique value.

GraphQlInputException by example: validating capacity

AddEventToFavorites doesn't need an example for this - it has no validation-worthy input fields beyond the already-checked ID. The missing validation becomes realistic instead for a future updateEventCapacity-style mutation. A fictional but realistic example:

use Magento\Framework\GraphQl\Exception\GraphQlInputException;

$capacity = (int) ($args['input']['capacity'] ?? 0);

if ($capacity < 0) {
    throw new GraphQlInputException(
        __('Capacity must not be negative, got "%1".', $capacity)
    );
}

The rule of thumb for telling GraphQlInputException apart from GraphQlAuthorizationException: if it's about "what" was passed (a business-invalid value), it's GraphQlInputException. If it's about "who" is making the request (missing permission), it's GraphQlAuthorizationException - regardless of how valid the passed values would be on their own.

Partial errors vs. a fully failed request

All four exception classes lead to a partial error: only the affected field becomes null (with null bubbling following the rules from chapter 7), while sibling fields in the same request execute normally. A purely syntactic error - a broken query that never even validates against the schema - instead causes the entire request to fail with no data field at all, before any resolver even runs.

{
  "data": {
    "addEventToFavorites": null
  },
  "errors": [
    {
      "message": "You must be logged in as a customer to favorite an event.",
      "extensions": { "category": "graphql-authorization" }
    }
  ]
}

Achtung: All four GraphQL exceptions expect a \Magento\Framework\Phrase as their first constructor parameter - created via __('...'), never a raw string. That's not a style preference: __() makes the error message translatable (i18n CSV files); a raw string stays identically German or English regardless of the storefront's language.

What actually reaches the client in production mode

All four exceptions introduced here deliver their message to the client unchanged - regardless of the deployment mode. That's intentional: these exceptions are meant for business-level messages that are meaningful and safe for the end user to see. A regular, non-GraphQL-specific exception, on the other hand, gets masked in production mode into the generic "Internal server error" message, so that no internal implementation details (stack traces, table names) leak out - in developer mode, it stays visible unmasked, which comes up again when debugging in chapter 25.

Tipp: If you're unsure whether an error case is business-level (one of the four GraphQL exceptions) or technical (a regular, unmasked exception in developer mode), ask yourself: "is this a situation an end user could cause and understand themselves?" An invalid customer input: yes, GraphQL exception. A failed database connection: no, let a regular exception propagate.

With systematic error handling in place, block 5 is complete - the events API reads, filters, writes, and protects correctly. Block 6 turns to performance topics: N+1 problems, caching, file uploads, and ACL.