# How to document null values: OpenAPI 3.1.0 drops nullable — use type: null?

Brady Weaver · October 26, 2026

> OpenAPI 3.1.0 drops nullable; use type: null in Swagger UI 5.x. pandas 3.0.6 splits dtype inference, and Jakarta Annotations 2.1.1 changes null checks.

| Takeaway | Detail |
| --- | --- |
| OpenAPI 3.1.0 drops `nullable` | Use `type: null` instead when documenting null values in Swagger UI 5.x. |
| pandas 3.0.6 splits dtype inference | pandas.array() will infer a nullable-integer dtype, while pandas.Series() uses different rules. |
| Jakarta Annotations API 2.1.1 shifts responsibility | Developers must read the documentation to determine when a null value is acceptable and whether a null check is necessary. |
| XML documentation can warn on optional+nullable | An optional and nullable parameter in XML documentation can trigger a compile warning that must be fixed. |

This guide verifies OpenAPI 3.1.0's removal of `nullable` for Swagger UI 5.x.

It shows when to use `type: null` with like-for-like checks.

![How to document null values](https://static.mm-ais.com/article-images-ai/how-to-document-null-values-openapi-3-1-ai-5a8ed5b3.jpg)

## How It Works

In OpenAPI 3.1.0, nullability is no longer a separate boolean flag. The specification aligns with JSON Schema, where null is a type, so a schema permits null by including "null" in its type declaration. A nullable string is therefore expressed as type: ["string", "null"], while a value that must be null is expressed as type: "null". The older nullable: true keyword belongs to the OpenAPI 3.0 style and should be treated as legacy when you are authoring or reviewing a 3.1 document.

The key terms are schema, the complete constraint set for a value; type, the declared data type or array of allowed types; null, both the JSON value and a schema type; nullable, the legacy 3.0 keyword that meant “this type may also be null”; and required, which controls whether a property must appear, not whether its value may be null. A property can be optional, nullable, both, or neither, and those are separate contract decisions.

| Term | Meaning in this context | 3.1 usage |
| --- | --- | --- |
| type: "null" | The value must be JSON null. | Use only for null-only fields. |
| type: ["string", "null"] | The value may be a string or null. | Use for nullable scalar fields. |
| required | The property must be present in the object. | Combine with a null type only when null is a valid present value. |

The mechanism changes documentation because the rendered contract now comes from the schema's type value rather than from a separate nullable flag. In Swagger UI 5.x, verify that a type array containing "null" is displayed as allowing null and that a type of only "null" is displayed as null-only. Because rendering and generator behavior can differ by exact version and configuration, verify the live page and generated code before committing.

Use type: "null" narrowly: when the field's valid value is JSON null itself, such as an explicit sentinel or a placeholder that is never replaced by another type. Do not use it as a shortcut for “this field may be missing”; omission is controlled by whether the property is listed in required. For a field that may contain a normal value or null, include "null" in a type array instead of replacing the normal type.

Before committing, compare like-for-like: inspect the same operation and schema in Swagger UI 5.x, confirm the displayed type, example, and required status match the intended contract, and check that downstream tooling interprets the null type the same way. If any rendered or generated result differs from the source schema, treat the schema as unverified rather than assuming the UI or generator is wrong.

## Key Factors to Consider

Before you commit a nullability change, treat the decision as a contract review, not a syntax edit. The three criteria that should decide the change are whether null is a legitimate business value for the field, whether the field is required or optional in the same request or response, and whether every downstream consumer can read the JSON Schema style type declaration that OpenAPI 3.1.0 uses. If any one of those is unclear, stop and verify the live rendered schema in Swagger UI 5.x before changing source files.

Use a like-for-like comparison: take one endpoint, one schema, and one example payload, then compare the committed document, the generated client, and the rendered UI for the same field. Do not compare a nullable field in one version with a non-null field in another, and do not compare optional behavior with required behavior. The useful check is whether the field's allowed values, required status, and example stay identical across all three views.

| Decision criterion | Verify before commit | Like-for-like check |
| --- | --- | --- |
| Null is a valid value | Business rule and example payload | Same field in document, UI, and client |
| Required versus optional | Parameter or property metadata | Same cardinality in all views |
| Consumer support | Generator and validator versions | Same OpenAPI 3.1.0 input |

The numbers that matter are the version identifiers already governing the work: OpenAPI 3.1.0 and Swagger UI 5.x. Treat them as compatibility gates, not decoration. If your generator, validator, or client library is pinned to an earlier OpenAPI version, a type array that includes null may be accepted by the document but mishandled by the tool. Verify the tool's supported version before committing, and record the verified combination in the change note.

Also verify the complete option, not a partial one. A field can be nullable, optional, required, deprecated, or read-only in different combinations, and each combination changes the client's null checks. Check the full schema object, the parameter or property metadata, and the example together. If the example omits the field while the schema allows null, that is a documentation gap even when the syntax is valid.

Finally, make the decision reversible. Keep the before and after schemas for the same endpoint, note which consumers were checked, and require a second reviewer to confirm that the rendered UI and generated code agree. The goal is not to choose the shortest declaration; it is to choose the declaration whose null behavior is unambiguous to the next maintainer.

## Common Mistakes

Pitfall one is importing an OpenAPI 3.0 habit into a 3.1 document: leaving nullable: true beside type: string. That keyword no longer carries the nullability meaning, so the contract can look familiar while validators and generated clients treat the field as non-null. Before committing, search the spec for nullable: and remove or replace each hit; where null is a real value, use a type array such as type: [string, null] instead of type: string, nullable: true.

Pitfall two is using type: null as if it meant "nullable." A schema whose type is only null accepts null and nothing else. If the field may contain a value or null, include both types, for example type: [string, null] for a name field. Reserve type: null for fields whose only valid value is null, such as a placeholder or tombstone marker, and document why that is intentional.

A third common mistake is confusing optional with nullable. required controls whether the property must appear; type controls what values are valid when it appears. An optional property with type: string may be omitted, but a present null still fails validation. If null is allowed, add "null" to the type and decide required status separately, rather than using one keyword to imply the other.

Swagger UI 5.x can make this easy to miss because the rendered operation may not announce nullability as clearly as the old flag did. Before committing, open the operation, expand the schema, and check the displayed type and example for the field. If the UI shows only string where null is allowed, the spec is still wrong even if the endpoint happens to accept null in practice.

Finally, do not rely on the schema alone to explain null. Once the nullable flag is gone, nothing in the document signals what null means for the field. Add a description that states what null means, whether clients must handle it, and whether it differs from an empty string or omitted value. That keeps the contract readable to the next maintainer instead of forcing them to infer intent from a bare type array.

## Insider Tactics

A non-obvious strategy is to treat type: null as a complete value contract, not as shorthand for "this field may be null." Reserve it for fields where null is the only valid payload, such as an explicit "no result" marker, and use an array type that includes both the real type and "null" when the field can hold either a value or null. That distinction keeps a null-only sentinel from being mistaken for an ordinary nullable field, and it makes review comments about intent easier to resolve.

Before committing, compare like-for-like totals and terms in the rendered documentation: the same endpoint, media type, schema, required marker, example, and nullability wording. Check that the example still matches the allowed types and that required status did not silently change. If any of those differs, the edit changed more than syntax and should be reviewed as a contract change.

| Check | Why it matters |
| --- | --- |
| Rendered type | Confirms the UI shows the intended nullability. |
| Example payload | Confirms the example still fits the allowed types. |
| Required marker | Confirms optional status did not change. |

Timing tip: make the specification edit and the Swagger UI 5.x verification part of the same change. Render the candidate document, inspect the schema's displayed type, example, and required marker, and reconcile any mismatch before the pull request is merged rather than after release. This prevents a valid but misleading document from reaching consumers.

For reviewers, request a diff of the rendered documentation in addition to the source diff. A valid OpenAPI 3.1 change can still alter how consumers read the contract, so the rendered view is the better evidence of what changed. Ask the author to name the one field whose nullability semantics changed and why.

If clients generate code from the specification, run the generator against the candidate document before merge and compare the resulting nullability annotations or types with the previous release. Treat a mismatch as a signal to pause and inspect the contract, not as a generator quirk. Record the comparison in the pull request so later reviewers can see the intended effect.

## Comparison

For this comparison, the winner is the declaration that keeps both the real value and null in one type array: use type: ["string", "null"] when a field can hold either a string or null, and reserve type: "null" for fields that can hold only null. That single rule settles most 3.1 migrations without adding a second keyword.

Side by side, the difference is small but exact: Option A carries 2 type entries, Option B carries 1 type plus 1 keyword from the older form, and Option C carries 1 type entry. The count matters because a reader that checks the type array sees 2 permitted shapes for A, 1 permitted shape plus an extra keyword for B, and 1 permitted shape for C.

| Option | Declaration | Type entries | Wins when |
| --- | --- | --- | --- |
| A | type: ["string", "null"] | 2 | Value or null is valid |
| B | type: "string" + nullable: true | 1 + 1 legacy keyword | Keeping a 3.0-shaped document |
| C | type: "null" | 1 | Only null is valid |

Option A wins for nullable business fields because it states both permitted outcomes in the same array. Option C wins for sentinel or placeholder fields where null is the entire contract. Option B wins only as a compatibility form: it keeps the older spelling, but it does not add a second permitted type in a 3.1 type array.

Before committing, compare like for like: check the same field in the source schema and in Swagger UI 5.x, and confirm that the number of permitted types in the UI matches the number in the declaration. If the UI shows 1 type where the schema declares 2, the null entry was dropped or the document is still being read as 3.0.

Use the table as a pre-commit gate: choose A for value-or-null fields, C for null-only fields, and B only when the document must remain 3.0-shaped. That keeps the comparison tied to the declaration rather than to a remembered keyword.

## What to do next

| Step | Action | Why it matters |
| --- | --- | --- |
| 1 | Verify the live, complete option by rendering the actual OpenAPI 3.1.0 document in Swagger UI 5.x and confirming null values use type: null. | Prevents committing a schema that Swagger UI 5.x cannot interpret correctly. |
| 2 | Compare like-for-like totals and terms across the same request and response shapes before and after removing nullable. | Ensures no hidden change in which null values are accepted or documented. |
| 3 | For nullable-integer columns, compare pandas 3.0.6 dtype inference between pandas.array() and pandas.Series() before documenting inferred types. | Avoids mismatched null documentation caused by different inference rules. |
| 4 | Read the Jakarta Annotations API 2.1.1 documentation to determine when a null value is acceptable and whether a null check is necessary. | Responsibility for null handling has shifted to developers. |
| 5 | Fix compile warnings in XML documentation for parameters that are both optional and nullable. | Keeps the build clean and keeps nullability documentation consistent. |
| 6 | Commit schema changes only after the live Swagger UI 5.x check and the like-for-like comparison are complete. | Follows the canonical decision rule before finalizing null-value documentation. |

## Frequently Asked Questions

**How is a nullable string expressed in OpenAPI 3.1.0?**

A nullable string is expressed as type: ["string", "null"].

**How is a value that must be null expressed in OpenAPI 3.1.0?**

A value that must be null is expressed as type: "null".

**Which nullability keyword is legacy in OpenAPI 3.1.0 documents?**

The older nullable: true keyword belongs to the OpenAPI 3.0 style and should be treated as legacy when authoring or reviewing a 3.1 document.

**What should be used instead of nullable when documenting null values in Swagger UI 5.x?**

Use type: null instead when documenting null values in Swagger UI 5.x.

**What dtype inference difference does pandas 3.0.6 introduce?**

pandas.array() will infer a nullable-integer dtype, while pandas.Series() uses different rules.

**What warning can XML documentation produce for an optional and nullable parameter?**

An optional and nullable parameter in XML documentation can trigger a compile warning that must be fixed.

## Quick answers

| What does OpenAPI 3.1.0 drop for documenting null values? | OpenAPI 3.1.0 drops `nullable` and uses `type: null` instead when documenting null values in Swagger UI 5.x. |
| --- | --- |
| How is a nullable string expressed in OpenAPI 3.1.0? | A nullable string is expressed as type: ["string", "null"]. |
| How is a value that must be null expressed in OpenAPI 3.1.0? | A value that must be null is expressed as type: "null". |
| How should the older nullable: true keyword be treated in a 3.1 document? | The older nullable: true keyword belongs to the OpenAPI 3.0 style and should be treated as legacy when authoring or reviewing a 3.1 document. |
| Why is nullability no longer a separate boolean flag in OpenAPI 3.1.0? | In OpenAPI 3.1.0, nullability is no longer a separate boolean flag, and the specification aligns with JSON Schema, where null is a type. |

Also worth reading: **Nullable fields in schemas: OpenAPI 3.1.0 type arrays vs nullable**: [Nullable fields in schemas: OpenAPI](https://specswriter.com/blog/nullable-fields-in-schemas-openapi-310-type-arrays-vs-nullable.php) · **OpenAPI 4.0's nullable: A Parse Failure Disguised as Spec Upgrade**: [OpenAPI 4.0's nullable: A Parse](https://specswriter.com/blog/openapi-40s-nullable-a-parse-failure-disguised-as-spec-upgrade.php) · **OpenAPI 3: One Nullable Keyword Deleted, Three Encodings Win**: [OpenAPI 3: One Nullable Keyword](https://specswriter.com/blog/openapi-3-one-nullable-keyword-deleted-three-encodings-win.php)

### Related reading

- [OpenAPI 3: One Nullable Keyword Deleted, Three Encodings Win](https://specswriter.com/blog/openapi-3-one-nullable-keyword-deleted-three-encodings-win.php)
- [OpenAPI 4.0's nullable: A Parse Failure Disguised as Spec Upgrade](https://specswriter.com/blog/openapi-40s-nullable-a-parse-failure-disguised-as-spec-upgrade.php)
- [Unlock Easy Technical Specification Document Creation With Templates](https://specswriter.com/blog/unlock-easy-technical-specification-document-creation-with-templates.php)
- [Word Document Signature Lines What You Need to Know](https://specswriter.com/blog/word_document_signature_lines_what_you_need_to_know.php)
- [7 Essential Components of a Software Development Scope of Work Document](https://specswriter.com/blog/7_essential_components_of_a_software_development_scope_of_wo.php)
- [Efficient Document Integration A Step-by-Step Guide to Merging Word Files While Preserving Complex Formatting](https://specswriter.com/blog/efficient_document_integration_a_step_by_step_guide_to_mergi.php)

### Latest

- [Nullable fields in schemas: OpenAPI 3.1.0 type arrays vs nullable](https://specswriter.com/blog/nullable-fields-in-schemas-openapi-310-type-arrays-vs-nullable.php)
- [Task test results: 2026 AI vs Darwin Information Typing Architecture (DITA)...](https://specswriter.com/blog/task-test-results-2026-ai-vs-darwin-information-typing-architecture-dita-1331-adopt.php)
- [DITA 1.3 vs freeform for a first task: the one-day spike test](https://specswriter.com/blog/dita-13-vs-freeform-for-a-first-task-the-one-day-spike-test.php)

Canonical: https://specswriter.com/blog/how-to-document-null-values-openapi-310-drops-nullable-use-type-null.php
Markdown: https://specswriter.com/blog/how-to-document-null-values-openapi-310-drops-nullable-use-type-null.php/index.md
