> For the complete documentation index, see [llms.txt](/llms.txt)

# Fatal diagnostics

## Fatal errors

The diagnostics above are *reported* — collected, sorted, and printed, with generation continuing
unless a config `error` blocks it. The codes below are different: they are **thrown**, so the command
stops at the first one and exits non-zero. They carry no severity because there is only one — fatal.

### Configuration loading

Raised before validation, when the config file itself cannot be read.

| Code | Meaning |
| --- | --- |
| `GLOTTO_CONFIG_NOT_FOUND` | No `glotto.yml` (or `glotto.yaml`) exists at the resolved path. Run the command from the directory holding your config, or point at it with `--config ./path/to/glotto.yml`. |
| `GLOTTO_CONFIG_YAML_PARSE` | The config file is not well-formed YAML. The error carries the `line` and `column` of the parse failure — a tab used for indentation and an unquoted `:` inside a value are the two usual causes. |

### The workspace file

Raised while reading a [`.glotto/workspace.json`](/docs/cli#the-workspace-file) found by walking up
from the current directory. A broken declaration is always reported — never quietly skipped in favour
of some further-up project, which would run the command against an API you did not name.

| Code | Meaning |
| --- | --- |
| `GLOTTO_WORKSPACE_INVALID` | The workspace file is not well-formed JSON, or does not match its schema — a missing `config`, a non-string path, or an unknown key. Unknown keys are rejected rather than ignored, so a typo cannot silently read as configured-and-working. |
| `GLOTTO_WORKSPACE_KEY_OWNED_ELSEWHERE` | The file sets `project` or `openapi_spec`. Neither is a workspace key in Glotto: `glotto.yml` already owns those values as `hosted.project` and `openapi.source`. The message names the key to move the value to. Most often seen when translating a Stainless `.stainless/workspace.json` by hand. |
| `GLOTTO_WORKSPACE_PATH_ESCAPE` | A declared `config` or `targets.<slug>` path is absolute, or escapes the workspace root with `..`. Declared paths are workspace-root-relative and must stay inside it. |
| `GLOTTO_WORKSPACE_CONFIG_MISSING` | The workspace file's `config` names a file that does not exist. Both the declared path and the resolved absolute path are named. |

### Editor-only notes

Surfaced by the Glotto language server in your editor. These never affect a CLI exit code.

| Code | Meaning |
| --- | --- |
| `GLOTTO_WORKSPACE_FOREIGN_CONFIG` | Information. The `glotto.yml` you have open is not the config declared by the `.glotto/workspace.json` above it — commands run from that workspace root will act on a different project. Add a workspace file beside this config to bind its own directory. Not a warning: the file still generates correctly. |

### Spec parsing and ingestion

Raised while reading your API description, before any IR is built.

| Code | Meaning |
| --- | --- |
| `GLOTTO_SPEC_UNSUPPORTED_VERSION` | The document declares an OpenAPI version Glotto does not support; 3.0.x and 3.1.x are supported. Swagger 2.0 is detected and auto-converted, so this names a genuinely unsupported version rather than an old one. |
| `GLOTTO_SPEC_PARSE` | The document could not be parsed. For OpenAPI the error carries the `line` and `column`; for GraphQL it carries the parser's own message verbatim. |
| `GLOTTO_SPEC_INVALID` | The document parsed but is not usable: a required field is missing, an input handed to the GraphQL ingest is not a GraphQL document, or the document nests deeper than the supported limit (a guard against adversarially nested input). The message names which. |
| `GLOTTO_SPEC_SWAGGER2_CONVERT` | A Swagger 2.0 document was detected but the in-process 2.0 → 3.0 conversion failed. Convert it to OpenAPI 3.x first — with `swagger2openapi`, or the editor.swagger.io "Convert to OpenAPI 3" command — and retry. |

### Spec source retrieval

Raised while **fetching** the spec, when
[`openapi.source`](/docs/glotto-yml#input-source--exactly-one-of-openapi--asyncapi--graphql) is not a plain
local file. Each names the source it failed on.

| Code | Meaning |
| --- | --- |
| `GLOTTO_SPEC_SOURCE_NOT_FOUND` | The spec source file does not exist at the given path. |
| `GLOTTO_SPEC_SOURCE_HTTP_STATUS` | A `url` source responded with a non-success HTTP status. The message names the final URL (after redirects) and the status. |
| `GLOTTO_SPEC_SOURCE_RESPONSE_TOO_LARGE` | A `url` source served a document larger than the 16 MiB response ceiling. The read stops at the limit rather than truncating, so nothing partial is ever parsed — host a smaller document, or point the source at a local file. |
| `GLOTTO_SPEC_SOURCE_AUTH_ENV_MISSING` | A source header references an environment variable that is not set in the running environment. Export it, or remove the header. |
| `GLOTTO_SPEC_SOURCE_FORBIDDEN_ADDRESS` | A `url` source was refused **before any connection was made**: its scheme is not `http(s)`, or its hostname resolves to a private, loopback, link-local, unique-local, or unspecified address. Checked on the initial URL and on every redirect hop. Use a publicly resolvable URL, or a local file source. |
| `GLOTTO_SPEC_SOURCE_GIT` | A `git` source operation failed. The message names the ref, the repository, and the underlying git error. |
| `GLOTTO_SPEC_SOURCE_COMMAND_SPAWN` | A `command` source binary could not be started — typically not on `PATH`. The message names the command. |
| `GLOTTO_SPEC_SOURCE_COMMAND_TIMEOUT` | The exporter command exceeded its wall-clock limit and was killed. |
| `GLOTTO_SPEC_SOURCE_COMMAND_OUTPUT_LIMIT` | The exporter command produced more output than the cap allows and was killed. |
| `GLOTTO_SPEC_SOURCE_COMMAND_EXIT` | The exporter command ran to completion but exited non-zero. The message carries the exit status and a bounded tail of its stderr. |
| `GLOTTO_SPEC_SOURCE_COMMAND_OUTPUT_PATH` | A `command` source's file-output `path` is absolute or escapes its working directory via `..`. Refused without reading outside the working directory; use a relative path inside it. |
| `GLOTTO_SPEC_SOURCE_COMMAND_NO_OUTPUT` | The exporter command succeeded but wrote no file at the declared output path. |
| `GLOTTO_SPEC_SOURCE_INTROSPECT_PATH` | An `introspect` source `path` does not exist or is not a directory. Refused before any analysis runs. |

### Transform engine

Raised while applying the [`transforms`](/docs/transforms) block to your spec. Each error carries the
`transform` name, its `transform_index` in your list, and the `target` it was resolving — so a failure
in a long transform chain names the exact entry.

| Code | Meaning |
| --- | --- |
| `GLOTTO_IR_TRANSFORM_TARGET_NOT_FOUND` | The transform's `target` matched nothing in the spec. Usually a typo or a schema renamed upstream. |
| `GLOTTO_IR_TRANSFORM_TARGET_AMBIGUOUS` | The `target` matched more than one candidate, so the transform will not guess. Narrow it until it names exactly one. |
| `GLOTTO_IR_TRANSFORM_INVALID_ARGUMENT` | An argument is not valid for that transform — the message names the argument and what it expected. |
| `GLOTTO_IR_TRANSFORM_SPEC_DEPTH_EXCEEDED` | The transform's walker hit the nesting-depth cap while traversing the spec (the same guard as `GLOTTO_SPEC_INVALID`'s depth check, applied during transformation). |
| `GLOTTO_IR_TRANSFORM_TARGET_INVALID_EXPRESSION` | An [`apply_overlay`](/docs/transforms#apply_overlay) action's JSONPath `target` is malformed or violates a rule the supported RFC 9535 subset refuses — for example, comparing a many-node query directly or using regex syntax outside I-Regexp. The message names the construct and its character offset. It is deliberately *not* reported as "matched nothing" — a target we cannot parse is a broken target, not a changed spec. |
| `GLOTTO_IR_TRANSFORM_OVERLAY_SOURCE_UNREADABLE` | An [`apply_overlay`](/docs/transforms#apply_overlay) or [`merge_document`](/docs/transforms#merge_document) entry references a document (`source:`) that could not be loaded — a missing file, an unreachable or blocked URL, a git ref that will not clone. The message carries the underlying reason. Check the path is relative to your `glotto.yml`, and that a URL host is publicly reachable. |
| `GLOTTO_IR_TRANSFORM_OVERLAY_DOCUMENT_INVALID` | A referenced overlay document loaded but is not an [Overlay Object](https://spec.openapis.org/overlay/latest.html) — unparseable YAML/JSON, a non-object root, or an `actions` that is missing, not an array, or empty. An empty overlay is treated as a broken reference rather than as "apply nothing", for the same reason a target matching nothing stops the build. |
| `GLOTTO_IR_TRANSFORM_MERGE_DOCUMENT_INVALID` | A [`merge_document`](/docs/transforms#merge_document) entry's document loaded but is not usable — unparseable YAML/JSON, a root that is not an object, or an empty object. A merge document is a *partial OpenAPI document*, so any key is legal, but it must be an object and it must declare at least one key: an empty one is a broken reference rather than "merge nothing". |
| `GLOTTO_IR_TRANSFORM_MERGE_DOCUMENT_INERT` | A [`merge_document`](/docs/transforms#merge_document) entry changed nothing — the spec already asserts every value the document declares, so the correction is dead. Usually this means your upstream spec has caught up with the override. Delete the entry, or point it at the document you meant. The check is per **entry**, not per key, so a document with one key that still bites is fine. |
| `GLOTTO_IR_TRANSFORM_MERGE_DOCUMENT_STRICT_VIOLATION` | A [`merge_document`](/docs/transforms#merge_document) entry set `strict: true`, which asserts the document only *corrects* and never *adds* — and at least one of its leaves names a path your spec does not have. The message lists **every** offending path, not just the first, so one run clears one round of typos. Usually a misspelled key (`descriptoin`), which without `strict` would have been silently added as a new node. Fix the path, or drop `strict` if the entry is genuinely meant to extend the spec. |
| `GLOTTO_IR_TRANSFORM_OVERLAY_EXTENDS_MISMATCH` | An overlay's `extends` names a different document than the URL your `openapi.source` points at, so its corrections were written for another API. The message carries both spellings. If the overlay does belong to this API, delete the optional `extends` field — Glotto always overlays the configured input source and never fetches `extends`; otherwise point `openapi.source` at the document the overlay was written for. Only checked when `openapi.source` is a URL: against a local file there is nothing comparable to check against, and the [verification report](/docs/verification-report) records that as `unverifiable` rather than implying a check that did not run. |
| `GLOTTO_IR_ALLOF_CONFLICT` | IR construction could not merge an `allOf` composition. Either two members declare the same property with structurally different types (the message names the property and both types), two members disagree on `type`, or the `allOf` chain is cyclic (the message names the cycle). Glotto refuses rather than picking one, because which member won would depend on declaration order in a way nothing in your generated SDK would reveal. Reconcile the two declarations in your spec, or split the conflicting property into distinct names. |
| `GLOTTO_CONFIG_ENUM_NAMING_ILLEGAL_IDENTIFIER` | An `enum_naming` entry in `glotto.yml` names an identifier that cannot compile in the language it is aimed at — it is not a legal identifier there, it is a reserved word in that language (`new` in Dart, Java, Kotlin, C#, PHP or Swift; a leading digit or punctuation anywhere), or it does not begin with an uppercase letter where that is what makes it a member at all — a Ruby name that does not start `[A-Z]` is a local variable rather than a constant, and a lowercase Go constant is unexported, so both compile or parse while declaring nothing your callers can reach. The message names the exact path so you can find it in your `glotto.yml`: `enum_naming.<model>.<value>.<slug>` when you pinned that language explicitly, and `enum_naming.<model>.<value>` when you wrote one logical name — there is no per-language key in your file in that case, so changing the logical name is the fix. Glotto refuses rather than quietly adjusting the name, because an identifier you wrote explicitly is one your callers read: silently emitting `new_` would give your SDK a public constant you never chose. Pick an identifier that compiles in that language, or drop the per-language entry and let Glotto derive the name. The check is per target, so a spelling that is legal in Python and reserved in Dart is refused only for Dart. |
| `GLOTTO_CONFIG_ENUM_NAMING_DUPLICATE_IDENTIFIER` | Two `enum_naming` entries for the same enum name the same identifier for the same target language, so the emitted enum would declare that member twice — `error[E0428]` in Rust, `CS0102` in C#, a duplicate `case` elsewhere. Each identifier is perfectly legal on its own, which is why this is a separate code from `GLOTTO_CONFIG_ENUM_NAMING_ILLEGAL_IDENTIFIER`: nothing here fails to compile in isolation. The message names the path of the second entry and the value that already holds the spelling. Glotto refuses rather than bumping one to `InProgress_`, because both names are ones you wrote and an explicit name is never auto-renamed. Pick a different identifier for one of them. The check is per target, so the same spelling for two different languages is not a collision. |
| `GLOTTO_IR_UNSUPPORTED_SCHEMA` | IR construction structurally rejected a schema shape it cannot represent. Not a transform failure — it is raised by the IR build itself, and the message names the offending schema. OpenAPI 3.1 type arrays may contain one concrete type plus `null`; use an explicit `oneOf` or `anyOf` for multiple distinct non-null types. |
