> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nitsor.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Every page in this documentation carries a `plans` list in its frontmatter and a page-details table naming the plans its content applies to: `All plans`, or a subset of `Community`, `Team` and `Enterprise`. A page limited to some plans also opens with a plan marker naming them.
> Use [Capabilities and limits](/product-status) for capability entitlements and documented availability and limits, and [Plans and billing](/plans-and-billing) for plan pricing and billing policy. Those pages are canonical; do not duplicate their policy values here.
> Reading this documentation grants no product access and no permission. No agent can create or approve a Nitsor release.

# Query language for agents

> Build and check NQL1 query trees and envelopes as JSON, and handle each outcome a query can have.

This page is for agents and the people who build them. [Query language (NQL1)](https://docs.nitsor.com/reference/query-language.md) defines what the text means; this page assumes it.

> [!NOTE]
>
> The dashboard, public HTTP API and Python SDK do not accept or evaluate NQL1 queries, and do not list query fields. [Capabilities and limits](https://docs.nitsor.com/product-status.md#limits) records this boundary.

An agent can use this page to write, check, store and explain queries. To check a query, validate its tree against the downloadable schemas, then apply the remaining rules on this page and in the language reference. [What validation covers](#what-validation-covers) lists those rules in order.

## Downloads

| File | What it holds |
| - | - |
| [query.schema.json](https://docs.nitsor.com/assets/nql1/query.schema.json) | JSON Schema (draft 2020-12) of a query tree. |
| [envelope.schema.json](https://docs.nitsor.com/assets/nql1/envelope.schema.json) | JSON Schema (draft 2020-12) of the envelope that fixes what a query runs over. |
| [errors.json](https://docs.nitsor.com/assets/nql1/errors.json) | Every refusal code with the message a person sees. |
| [examples.json](https://docs.nitsor.com/assets/nql1/examples.json) | Example queries as text, canonical text and tree. |

The schemas check structure only, and they leave out some of the structural rules Nitsor applies, so a document that passes them can still be refused. [What validation covers](#what-validation-covers) lists the rules they omit. The example selectors and values illustrate syntax and do not establish that a field exists, is visible to you, or can be evaluated.

## One query, three forms

The same query as a person states it, as NQL1 text, and as the tree an agent exchanges. It illustrates the language and cannot run: Nitsor does not evaluate relation blocks, and the example keeps one because it is the clearest way to show the `relation` node. [Capabilities and limits](https://docs.nitsor.com/product-status.md#limits) records this.

> [tab] Intent
>
> Scans with at least one accepted Crack label, whose Source was registered from the start of the day a week ago through the end of today, in the envelope's time zone.
>
> A week ago is exactly 7 × 24 hours before `asOf`. That usually gives the seven days before today and today, but a clock change during the week can move the first day by one. [Dates, durations and time zones](https://docs.nitsor.com/reference/query-language.md#dates-durations-and-time-zones) has the rule.

> [tab] Text
>
> ```text nql1 wrap theme={"system"}
> label(class:Crack AND reviewState:accepted) AND source.registered:[now-7d/d TO now/d]
> ```
>
> This is also its canonical text.

> [tab] Tree
>
> ```json query.json theme={"system"}
> {
>   "ql": "nql1",
>   "expression": {
>     "kind": "and",
>     "children": [
>       {
>         "kind": "relation",
>         "relation": "label",
>         "expression": {
>           "kind": "and",
>           "children": [
>             { "kind": "compare", "field": ["class"], "op": "eq", "value": { "kind": "text", "value": "Crack" } },
>             { "kind": "compare", "field": ["reviewState"], "op": "eq", "value": { "kind": "text", "value": "accepted" } }
>           ]
>         }
>       },
>       {
>         "kind": "range",
>         "field": ["source", "registered"],
>         "lower": {
>           "value": { "kind": "relativeDate", "offset": { "amount": -7, "unit": "d" }, "roundToDay": true },
>           "inclusive": true
>         },
>         "upper": {
>           "value": { "kind": "relativeDate", "offset": null, "roundToDay": true },
>           "inclusive": true
>         }
>       }
>     ]
>   }
> }
> ```

Text and tree convert both ways without loss. Store and compare canonical text or the tree, never a spelling a person typed.

## The query tree

A query is `{ "ql": "nql1", "expression": ... }`. `expression` is `null` for the empty query, which means the whole authorised, selected scope, or one node. Every object is strict: a property the contract does not define is refused.

| `kind` | Properties | Rules |
| - | - | - |
| `and`, `or` | `children` | At least two children. A child is never of the same kind as its parent: `a AND (b AND c)` is one `and` with three children. |
| `not` | `expression` | One node. |
| `relation` | `relation`, `expression` | `relation` is `label`, `source` or `task`. `expression` holds no relation, at any depth. |
| `has` | `field` | |
| `compare` | `field`, `op`, `value` | `op` is `eq`, `ne`, `gt`, `gte`, `lt` or `lte`. |
| `range` | `field`, `lower`, `upper` | Each side is `{ "value": scalar, "inclusive": boolean }` or `null` for an open side. At least one side is not `null`. |
| `prefix` | `field`, `value` | `value` is the literal before `*`: at least three bare-word characters. |
| `set` | `field`, `quantifier`, `values` | `quantifier` is `any` or `all`; `values` holds at least one scalar. |
| `contains` | `field`, `value` | `value` is the literal substring, a string. |

A `field` is an array of strings. The first is an identifier that is not a keyword or `has`; each later one is any non-empty string. `meta["machine.name"]` is `["meta", "machine.name"]`, and `meta.machine.name` is `["meta", "machine", "name"]`.

### Scalars

| `kind` | Properties | Example |
| - | - | - |
| `text` | `value`: string | `{ "kind": "text", "value": "weld-ct-v2" }` |
| `number` | `value`: canonical decimal text | `{ "kind": "number", "value": "0.12" }` |
| `boolean` | `value`: `true` or `false` | `{ "kind": "boolean", "value": false }` |
| `date` | `value`: `YYYY-MM-DD` | `{ "kind": "date", "value": "2026-09-01" }` |
| `relativeDate` | `offset`: `{ amount, unit }` or `null`; `roundToDay`: boolean | `{ "kind": "relativeDate", "offset": null, "roundToDay": true }` is `now/d` |
| `duration` | `amount`, `unit` | `{ "kind": "duration", "amount": 48, "unit": "h" }` |

Units are `m`, `h`, `d` and `w`.

### Numbers are strings

A `number` scalar carries its value as decimal text, so no value loses precision in a JSON parser that reads numbers as floating point. The text must already be canonical: no trailing fractional zeros, no leading zeros, no `-0`, no exponent. `"1.5"` is accepted and `"1.50"` is refused with `invalid_ast`.

Two integers in the tree are JSON numbers: a relative date's `offset.amount`, which is never `0` (write `"offset": null` instead), and a duration's `amount`, which is never negative.

```json query.json theme={"system"}
{
  "ql": "nql1",
  "expression": {
    "kind": "and",
    "children": [
      {
        "kind": "compare",
        "field": ["source", "spacing", "z"],
        "op": "lte",
        "value": { "kind": "number", "value": "0.5" }
      },
      {
        "kind": "compare",
        "field": ["task", "waited"],
        "op": "gt",
        "value": { "kind": "duration", "amount": 48, "unit": "h" }
      }
    ]
  }
}
```

This is `source.spacing.z:<=0.5 AND task.waited:>48h`.

### Only canonical trees are accepted

A tree is accepted only if it is exactly the tree Nitsor's NQL1 parser produces from its own canonical text. In practice:

| A builder must not send | Because | Refused with |
| - | - | - |
| an `and` inside an `and`, or an `or` inside an `or` | same-kind children are flattened | `invalid_ast` |
| an `and` or `or` with one child | a group of one leaves no node | `invalid_ast` |
| a `relation` inside a `relation` | relation blocks do not nest | `invalid_ast` |
| `{ "kind": "number", "value": "1.50" }` | numbers are canonical | `invalid_ast` |
| `"offset": { "amount": 0, "unit": "d" }` | a zero offset is no offset | `invalid_ast` |
| an unknown `kind` or an extra property | objects are strict | `invalid_ast` |
| `{ "kind": "date", "value": "2026-02-29" }` | the day does not exist | `invalid_date` |
| a `range` with both sides `null` | a range needs a bound | `range_unbounded` |

A data refusal carries a `path`: the keys and indexes from the top of the document to the offending value, such as `["expression", "children", 8]`.

## What validation covers

Schema validation is the first structural check, not the last. A query passes through these layers in order, and each one refuses on its own terms:

| Layer | It checks | Refusal codes or outcome |
| - | - | - |
| Bounded copy | The input is plain JSON within size bounds, before any schema reads it. | `depth_exceeded`, `data_too_large`, `sparse_array`, `non_plain_data` |
| Schema | Shape, node kinds, strict properties, canonical spellings. The two downloadable schemas check most of this layer and nothing outside it. | `invalid_ast`, `invalid_envelope` |
| Limits | Depth, filters, relation blocks, set values and OR alternatives on the tree. | `depth_exceeded`, `predicates_exceeded`, `relations_exceeded`, `set_values_exceeded`, `or_branches_exceeded` |
| Canonical round trip | Real calendar days, range bounds of one kind, the 4 KiB canonical text limit, and that the canonical text parses back to this tree. | `invalid_date`, `date_out_of_range`, `range_unbounded`, `range_kind_mismatch`, `query_too_long`, `invalid_ast` |
| Time | Each date and relative date resolves in the envelope's zone at its `asOf`. | `time_zone_unavailable`, `calendar_day_not_contiguous`, `relative_date_out_of_range` |
| Field registry and access | Each field exists in the registry revision, you may see it, it admits the operator, and the value has its type. | [unavailable](#unavailable) |
| Planner | Nitsor has an evaluation plan for this shape over this resource, scope and version. | [unsupported](#unsupported) |
| Evaluation | The read finished, or stopped at a budget. | no refusal; [complete](#complete) or [partial](#partial) |

A document that passes the downloadable schemas meets the constraints written in them, and no more. Nitsor's validator applies further rules at the Schema layer that the downloads leave out. It refuses a query that breaks one with `invalid_ast`, and an envelope with `invalid_envelope`, before any later layer runs. These rules include:

| Rule the downloads leave out | Defined in |
| - | - |
| A `field` holds at least one string. | [The query tree](#the-query-tree) |
| A relative date's `offset.amount` is never `0`. | [Only canonical trees are accepted](#only-canonical-trees-are-accepted) |
| `labelPolicy.reviewStates` lists each state once, in contract order. | [The envelope](#the-envelope) |
| `asOf` falls from 1583-01-01 to 9999-12-31. | [The envelope](#the-envelope) |

JSON Schema also does not check the bounded-copy admission before the Schema layer, such as plain data or the total bytes of all strings, or any layer after it. An agent that validates locally with the schemas should still expect every refusal in this table.

### Trees sent as data

A tree or envelope received as JSON is copied into a bounded snapshot before any schema runs, so hostile nesting is refused rather than recursed into. These bounds sit well above any valid query's size:

| Bound | Value | Refused with |
| - | - | - |
| Nesting of objects and arrays | 20 | `depth_exceeded` |
| Entries in one array or object | 4,096 | `data_too_large` |
| Values in the whole document | 32,768 | `data_too_large` |
| Total string content | 64 KiB | `data_too_large` |
| An array with a hole | none allowed | `sparse_array` |
| Anything JSON cannot carry: accessors, class instances, functions, `undefined`, non-finite numbers | none allowed | `non_plain_data` |

For data, `depth_exceeded` can come from this nesting bound or from the tree depth limit of 8 that the Limits layer applies. Both carry a `path` and the same message, so the refusal does not say which bound was reached; compare the document with both bounds.

## The envelope

The envelope holds the expression and everything that fixes its universe. The same expression means the same predicate in every envelope.

| Property | Required | Value |
| - | - | - |
| `ql` | yes | `"nql1"`. Any other version is refused. |
| `resource` | yes | `"scans"` returns Scans; `"tasks"` returns Tasks. |
| `scope` | yes | `{ "kind": "project", "projectId": ... }` |
| `version` | yes | Where labels and Sources are read: `{ "kind": "branch", "branchId", "commitId" }` (a branch pinned at a commit), `{ "kind": "commit", "commitId" }` or `{ "kind": "datasetVersion", "datasetVersionId" }`. |
| `timeZone` | no, default `"UTC"` | An IANA zone name exactly as the pinned tz database table spells it, or `"UTC"`. |
| `asOf` | yes | The pinned instant `now` stands for, in UTC with milliseconds, such as `"2026-10-25T09:30:00.000Z"`, from 1583-01-01 to 9999-12-31. |
| `labelPolicy` | no, default `{ "reviewStates": ["accepted"] }` | The review states label fields and `label(...)` read: one or more of `accepted`, `corrected`, `draft`, `rejected`, each once, in that order. |
| `registryRevision` | yes | The field registry revision the expression was checked against. |
| `sort` | yes | 1 to 4 keys, each `{ "field": [...], "direction": "asc" }` or `"desc"`. |
| `expression` | yes | A node, or `null`. |

Identifiers in `scope` and `version` are strings of 1 to 200 characters. Write every property, defaults included, so the envelope reads the same everywhere it is stored.

A complete envelope over Tasks, with synthetic identifiers:

```json envelope.json theme={"system"}
{
  "ql": "nql1",
  "resource": "tasks",
  "scope": { "kind": "project", "projectId": "proj_turbine_blades" },
  "version": { "kind": "branch", "branchId": "branch_main", "commitId": "commit_0192" },
  "timeZone": "Europe/Berlin",
  "asOf": "2026-10-25T09:30:00.000Z",
  "labelPolicy": { "reviewStates": ["accepted"] },
  "registryRevision": "reg_2026_10_25",
  "sort": [
    { "field": ["task", "created"], "direction": "asc" },
    { "field": ["task", "id"], "direction": "asc" }
  ],
  "expression": {
    "kind": "and",
    "children": [
      {
        "kind": "set",
        "field": ["task", "state"],
        "quantifier": "any",
        "values": [
          { "kind": "text", "value": "queued" },
          { "kind": "text", "value": "assigned" }
        ]
      },
      {
        "kind": "compare",
        "field": ["task", "created"],
        "op": "gte",
        "value": { "kind": "relativeDate", "offset": { "amount": -7, "unit": "d" }, "roundToDay": true }
      }
    ]
  }
}
```

Its expression is `task.state:ANY(queued, assigned) AND task.created:>=now-7d/d`: queued or assigned Tasks created on or after the start of 18 October in Berlin (`2026-10-17T22:00:00.000Z`), oldest first.

An envelope can be structurally valid and still unsupported. `"resource": "tasks"` with `"version": { "kind": "datasetVersion", ... }` passes the schema, but Task facts are live, so Nitsor has no plan for Tasks frozen in a Dataset Version.

### Relative intent and pinned evaluation

`now-7d/d` in query text is intent: "since the start of the day a week ago", whenever it runs. An envelope's `asOf` turns that intent into one evaluation. Every `now` in that envelope means the same instant, so bounds stay fixed while the results are paged.

* **Store the text or tree as the intent.** Do not replace relative dates with the instants they resolved to, unless the intent really was a fixed window.
* **Treat an envelope as the record of one evaluation.** Running the query again means a new `asOf`, and a new evaluation with a fresh cursor.
* **Do not read an old `asOf` as the present.** An envelope from yesterday answers yesterday's "today".

Pinning `asOf` fixes the time bounds. It does not freeze the data. `version` decides where labels and Sources are read, and a commit or Dataset Version pins them. Task facts such as state, Stage and time waited are live workflow facts: a branch commit in the envelope pins the branch context, not a historical snapshot of the Tasks. Two evaluations of the same Tasks envelope can return different Tasks, and a queued Task's time waited is measured up to `asOf`.

## Outcomes and what to do

A query ends in one of five outcomes, and each calls for a different response. Confusing them is the most common way an agent reports something false.

An unexpected execution or service failure is an error, not an outcome. Never report it as an empty, partial or complete result.

No public interface returns these outcomes. They are the classes Nitsor's query evaluation keeps apart, and an agent that reasons about NQL1 should keep them apart too.

| Outcome | It means | Words to use |
| - | - | - |
| [Refused](#refused) | The query is not valid NQL1. | The query has an error at this point: … Then give the fix. |
| [Unavailable](#unavailable) | A field is unknown or not visible to you. | That field is not available to me. Say nothing about whether it exists. |
| [Unsupported](#unsupported) | The query is valid, but Nitsor has no plan to evaluate it. | Nitsor cannot evaluate this query shape here. |
| [Partial](#partial) | Evaluation stopped at a budget. The rows may be empty. | The search did not finish, so these results may be incomplete. Give a count only as "at least N", and only when the result carries one. |
| [Complete](#complete), with rows | Evaluation was not cut short, and the page holds rows. | These records match. Say "N records match" only when the count is exact, which only a first page that examined every candidate carries. |
| [Complete](#complete), empty first page | The first page examined every candidate and returned no rows. An empty later page does not prove this. | No records match. |

### Refused

The text or data is not valid NQL1. The refusal carries a code, and a span for text or a path for a tree or envelope. Repair the query using the code, change one thing at a time, and check it again before you use it. Never resend it unchanged. [Repairable refusals](#repairable-refusals) maps the common codes to their fixes.

### Unavailable

A field cannot be used. A field that does not exist and a field you may not see give the same answer, by design. Do not try spelling variants to find out which, and do not infer from a refusal that a field, a value or a count exists. Use a field whose definition you have read, such as one in [Fields used in the examples](https://docs.nitsor.com/guides/discover-query-fields.md#fields-used-in-the-examples), or ask the person you work for which field to use and to arrange access.

### Unsupported

The query is valid and its fields are available, but Nitsor has no plan to evaluate that shape over that resource, scope or version. A query checked against an older registry revision is also unsupported until it is checked again against the current one. Report the limit. Do not change the query's meaning to get a plan: replacing `NOT field:value` with `field:!=value`, or splitting a `label(...)` block into flat filters, asks a different question.

### Partial

Evaluation stopped at a budget before it examined everything. The rows returned are real, and the count is a lower bound or unknown. Continue from the cursor with the same envelope, or report the result as incomplete. Never report a partial result as the total, and never report an empty partial page as no match.

### Complete

Evaluation was not cut short by a budget. Only a first page that examined every candidate carries an exact count, and only an empty one proves that nothing matches. Later pages carry no total, because the records can change between pages.

## Repairable refusals

These codes mean the text is wrong in a way an agent can fix without asking anyone:

| Code | Typical cause | Repair |
| - | - | - |
| `whitespace_in_predicate` | `class: Crack` | Remove the space: `class:Crack`. |
| `missing_operator` | two filters side by side | Join them with `AND` or `OR`. |
| `keyword_case` | `and`, `or`, `not`, `to`, `any`, `all` | Write keywords in capitals, or quote the word to search for it. |
| `expected_colon` | a bare word, as in free-text search | Name the field: `field:value`. |
| `invalid_value` | `1.2.840.10008`, `CT-2.5`, `0.5mm`, `1e5` | Quote text; drop units from numbers; write numbers in full, as `100000`. |
| `invalid_number` | `007`, `0512` | Drop the leading zeros: `7`, `512`. |
| `invalid_date` | `2026-9-1`, `2026-02-29` | `YYYY-MM-DD`, a day that exists. |
| `unsupported_time_unit` | `now-1M`, `now-1y`, `5s` | Use `m`, `h`, `d` or `w`, or a date. |
| `null_literal` | `field:null` | `NOT has:field`. |
| `invalid_prefix` | `*blade`, `bla*de` | Put the `*` at the end: `blade*`. |
| `comparator_with_set` | `field:!=ANY(a, b)` | `NOT field:ANY(a, b)`. |
| `range_unbounded` | `[* TO *]` | `has:field`, or give a bound. |
| `range_kind_mismatch` | a number and a date as bounds | Use one kind on both sides. |
| `relation_space` | `label (class:Crack)` | `label(class:Crack)`. |
| `nested_relation` | `label(source(...))` | Separate blocks joined by `AND`. |
| `predicates_exceeded`, `or_branches_exceeded`, `set_values_exceeded`, `relations_exceeded`, `depth_exceeded` | a query past a limit | Simplify the query; a limit never truncates it. |

Two codes come from the envelope's zone, not the text. For `calendar_day_not_contiguous`, use timestamps for that day. `time_zone_unavailable` means the zone is on the pinned tzdata 2026c list but the runtime evaluating the query cannot resolve it. Report that, or ask the person you work for whether `UTC` or another listed zone fits; a different zone moves every day boundary. [Dates, durations and time zones](https://docs.nitsor.com/reference/query-language.md#dates-durations-and-time-zones) has the zone rules.

## Refusal codes

Every code NQL1 returns, grouped by where it arises. [errors.json](https://docs.nitsor.com/assets/nql1/errors.json) has the same list for machines.

> [accordion] Text and lexical rules
>
> | Code | Message |
> | - | - |
> | `query_too_long` | This query is longer than 4 KiB as written in canonical form. Shorten it or split it into saved views. |
> | `unexpected_character` | This character cannot appear here. Quote values that contain it. |
> | `unexpected_end` | The query ends too early. Add a filter here. |
> | `unterminated_string` | This quoted value has no closing ". |
> | `invalid_string` | Use JSON escapes inside quotes: \\" \\\ \n \t \uXXXX. |

> [accordion] Structure
>
> | Code | Message |
> | - | - |
> | `missing_operator` | Join filters with AND or OR. nql1 never assumes AND. |
> | `keyword_case` | Write AND, OR, NOT, TO, ANY and ALL in capitals. To search for the word itself, quote it: "and". |
> | `unexpected_keyword` | This word is reserved here. Quote it to search for the word itself. |
> | `unbalanced_parenthesis` | This ) has no matching (. |
> | `unclosed_group` | This ( has no matching ). |
> | `empty_group` | Parentheses need a filter inside. |
> | `expected_filter` | Start a filter with a field name, for example class:Crack. |
> | `unknown_relation` | Only label(...), source(...) and task(...) group filters on one related record. |
> | `nested_relation` | A label, source or task block cannot contain another block in nql1. |
> | `relation_space` | Remove the space: write label(...), source(...) or task(...) with ( right after the name. |

> [accordion] Fields
>
> | Code | Message |
> | - | - |
> | `expected_colon` | Follow the field with : and a value, for example class:Crack. nql1 has no free-text search. |
> | `whitespace_in_predicate` | Remove the space: write field:value with nothing between them. |
> | `invalid_field` | Field names are words joined by dots, with bracketed keys for anything else: meta\["Inspection protocol"]. |
> | `reserved_field` | This word cannot start a field name. Use a bracketed key instead. |
> | `empty_field_key` | A bracketed key needs at least one character. |

> [accordion] Values
>
> | Code | Message |
> | - | - |
> | `expected_value` | Add a value after the operator. |
> | `invalid_value` | Quote values that contain spaces or punctuation: "like this". |
> | `invalid_comparator` | Use one of = != > >= \< \<=. |
> | `comparator_with_set` | A comparator takes one value, not ANY(...) or ALL(...). To exclude several values, write NOT field:ANY(a, b). |
> | `invalid_number` | Write numbers without leading zeros or exponents, for example 0.12 or 48. |
> | `invalid_date` | Use a real calendar day as YYYY-MM-DD, for example 2026-09-01. |
> | `date_out_of_range` | Use a day from 1583-01-01 to 9999-12-31. |
> | `invalid_relative_date` | Write relative dates as now, now-7d, now+2h or now-7d/d. |
> | `relative_date_out_of_range` | This relative date lands outside 1583-01-01 to 9999-12-31. Use a smaller offset. |
> | `unsupported_time_unit` | Use m, h, d or w. Calendar months and years are not in nql1. |
> | `null_literal` | nql1 has no null. Use NOT has:field to find records without a value. |
> | `invalid_prefix` | Only a trailing \* after a plain word is a wildcard, for example blade\*. |
> | `prefix_too_short` | A prefix needs at least three characters before the \*. |
> | `invalid_range` | Write ranges as \[low TO high]. Use \{ or } to leave a bound out. |
> | `range_unbounded` | A range needs at least one bound. Use has:field to find any value. |
> | `range_kind_mismatch` | Both bounds of a range need the same kind of value. |
> | `invalid_set` | Write ANY(a, b) or ALL(a, b) with at least one value and no trailing comma. |

> [accordion] Complexity limits
>
> | Code | Message |
> | - | - |
> | `depth_exceeded` | This query nests too deeply. Use at most 8 levels. |
> | `predicates_exceeded` | This query has too many filters. Use at most 32. |
> | `relations_exceeded` | This query has too many label, source or task blocks. Use at most 8. |
> | `set_values_exceeded` | This query lists too many values. Use at most 32 across ANY and ALL. |
> | `or_branches_exceeded` | This query has too many OR alternatives. Use at most 8. |

> [accordion] Trees and envelopes sent as data
>
> | Code | Message |
> | - | - |
> | `invalid_ast` | This query tree does not match the nql1 contract. |
> | `data_too_large` | This query data is too large to check. Send a smaller tree. |
> | `sparse_array` | This query data has an array with a missing entry. Send every entry. |
> | `non_plain_data` | This query data must be plain JSON values: objects, arrays, strings, numbers, true, false and null. |
> | `invalid_envelope` | This query envelope does not match the nql1 contract. |

> [accordion] Resolving times
>
> | Code | Message |
> | - | - |
> | `calendar_day_not_contiguous` | In this time zone the clocks went back over midnight, so this day is two separate periods. Use timestamps instead. |
> | `time_zone_unavailable` | This time zone is not available here. Use a zone from the nql1 list. |

The `query_too_long` message mentions saved views. That is the catalog's wording, and no saved-view interface accepts NQL1. Shorten the query instead.

## Next step

Read a field's definition before you choose fields.

> [card] [Read a query field](https://docs.nitsor.com/guides/discover-query-fields.md)
>
> See which filters a field admits, what its values mean and whether Nitsor can evaluate a query on it.

> [card] [Query recipes](https://docs.nitsor.com/guides/query-recipes.md)
>
> Start from worked queries for common questions.

<table aria-label="Page details">
  <tbody>
    <tr><th scope="row">Plans</th><td>All plans</td></tr>
    <tr><th scope="row">Last verified</th><td>2026-10-25</td></tr>
  </tbody>
</table>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.