← Debugging Guides

阅读中文版本

GraphQL Non-Null Field Returned Null — Error Propagation

Fix "Cannot return null for non-nullable field" by finding the resolver returning null, tracing error propagation, and aligning schema nullability.

intermediatedebugginggraphqlschemaerrors

Symptoms

  • The response contains "Cannot return null for non-nullable field User.name"
  • A whole parent object becomes null because one field failed
  • The client sees a partially null response with errors

Possible causes

  • The resolver returns null for a field declared non-null in the schema
  • An async resolver rejected and the error propagated past a non-null boundary
  • The data source legitimately lacks the value but the schema was marked non-null

Diagnosis steps

Read the errors array for the field path

curl -s -X POST https://api.example.com/graphql -H 'Content-Type: application/json' -d '{"query":"{ user { name } }"}'

Interpretation

The error path identifies the exact field resolver that returned null.

Add resolver-level logging

git grep -n "User:\|name:" src/graphql/resolvers

Interpretation

Logging inside the resolver confirms whether it returns null or throws.

Root cause

GraphQL propagates null upward through non-null fields: if a non-null field resolves to null, the error bubbles to the nearest nullable parent, which becomes null. This protects the client from partial objects but can null more than expected.

Fix

Fix the resolver to return a non-null value, make genuinely optional fields nullable in the schema, and handle errors in resolvers so a single failure does not null an entire parent.

Risk note

Making fields nullable to avoid the error pushes null handling to every client; fix the data or the resolver instead when the value is required.

Verify the fix

Rerun the query and confirm the field is populated and errors is empty; verify a genuine failure still surfaces a clear error.

Symptoms

The error “GraphQL Non-Null Field Returned Null — Error Propagation” points at a specific failure mode. This guide documents the symptoms you can observe, the commands that isolate the cause, the root cause, the fix, and the checks that prove the fix works.

Observed symptoms include:

  • The response contains “Cannot return null for non-nullable field User.name”
  • A whole parent object becomes null because one field failed
  • The client sees a partially null response with errors

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Read the errors array for the field path

curl -s -X POST https://api.example.com/graphql -H 'Content-Type: application/json' -d '{"query":"{ user { name } }"}'

The error path identifies the exact field resolver that returned null.

Step 2: Add resolver-level logging

git grep -n "User:\|name:" src/graphql/resolvers

Logging inside the resolver confirms whether it returns null or throws.

Likely contributors, in order of frequency:

  • The resolver returns null for a field declared non-null in the schema
  • An async resolver rejected and the error propagated past a non-null boundary
  • The data source legitimately lacks the value but the schema was marked non-null

Root Cause

GraphQL propagates null upward through non-null fields: if a non-null field resolves to null, the error bubbles to the nearest nullable parent, which becomes null. This protects the client from partial objects but can null more than expected.

The Fix

Fix the resolver to return a non-null value, make genuinely optional fields nullable in the schema, and handle errors in resolvers so a single failure does not null an entire parent.

Watch out: Making fields nullable to avoid the error pushes null handling to every client; fix the data or the resolver instead when the value is required.

type User {
  id: ID!
  name: String!      # non-null: resolver must never return null
  bio: String        # nullable: may be absent
}

// resolver: satisfy the non-null contract or throw a field error
User: {
  name: (u) => {
    if (!u.name) throw new Error('User.name is required');
    return u.name;
  },
}

Verify and Prevent

Rerun the query and confirm the field is populated and errors is empty; verify a genuine failure still surfaces a clear error.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.