Docs/Timebase Components/Validation Rules Reference
AtlasReference

Validation Rules Reference

Every validation rule Atlas applies to a domain, the message each one emits, and what to change to satisfy it.

Updated 26 Oct 2026Timebase Docs

Overview#

When Atlas checks your domain it produces violations, not warnings. A violation is a single problem found in your ontology.jsonld file or in one of your *.map.json instance map files, tagged with a rule ID such as I-B3 so you can look it up here.

Violations are hard-blocking, not advisory. Atlas validates your whole domain before it builds anything. If a single Error-severity violation is present, the build stops: you get an error response with the list of violations, and no world is built at all. Nothing is partially compiled, and no type or instance is "excluded" while the rest goes through. It is all or nothing.

The practical consequence: while you still have Error violations outstanding, fixing one of them will not make part of your model appear. Nothing appears until the last Error is gone. Until then, whatever was successfully compiled last continues to answer queries.

Almost every rule in Atlas is Error severity. There is exactly one Warning-severity diagnostic (I-B22, an unbound function parameter), and for reasons explained below you can never see it in a compile response.

Libraries are checked by the same rules through the same pipeline. Every O-A and O-B rule in this article applies to a library's ontology.jsonld as well as to a domain's.


Where violations show up#

This is the most important thing on the page, because saving and compiling fail in completely different ways. Do not assume that a successful save means your content is valid.

What you did What happens when violations are present How you find out
Save an ontology or instance map HTTP 2xx (success). The content lands in your own private draft session. The target branch does not advance. The Atlas-Validation-Violations response header, carrying a count only. The header is absent (not 0) when the save is clean.
Compile (PUT .../world) HTTP 422 with Content-Type: application/problem+json and a violations[] array. No world is built. The previously compiled world keeps serving queries. The violations[] array in the 422 body.
Save through the browser interface The same. The drawer reports Saved, and the change sits in your draft session rather than on the branch. The domain's Overview lists every outstanding violation under Outstanding items: the rule, its message, the file and pointer, and a Show in builder link to the element at fault.
Ask (GET .../validation) HTTP 200 with the current list, whether or not anything is wrong. Compiles nothing, caches nothing. The violations[] array in the 200 body, plus errorCount, warningCount and blocking.

Saving succeeds even when the content is broken#

Atlas draft sessions are on by default. A save that fails validation is still written, but it is written into your own draft session rather than onto the branch. You get a normal success status code back. The only in-band signal that anything is wrong is the response header:

Atlas-Validation-Violations: 7

That header carries a count and nothing else. To see the actual list, use the validation read described below, or open the domain's Overview in the browser.

The New Entity Type drawer after saving a type that uses an unpinned union property: the drawer reports Saved, and the problem is reported on the Overview insteadThe New Entity Type drawer after saving a type that uses an unpinned union property: the drawer reports Saved, and the problem is reported on the Overview instead

A save with a violation still reports Saved. The problem shows up on the domain's Overview, not in the drawer. When you are ready to move the work onto the branch, publish the draft explicitly:

POST /api/v1/branches/{branch}/draft/publish

Publish is branch-scoped. It promotes everything the session holds, across every domain and library you changed on that branch, and it revalidates the merged result as a whole. Read GET /api/v1/branches/{branch}/draft first if you need to know what that covers: changedDomains, changedLibraries and otherChangeCount name exactly what the publish will move, deletions included.

Note: a compile run inside a draft session builds your copy and leaves the shared compiled world in place, so a save and a compile can both succeed while your query results are unchanged. Publish the draft to make the change visible to everyone else.

There is one exception to a save being held in a draft: if rule O-A1 or O-A2 applies, the save is refused. Those two mean the ontology file cannot be read at all, so there would be nothing to edit your way out of.

Compiling returns 422 and builds nothing#

The compile route is:

PUT /api/v1/branches/{branch}/domains/{domain}/world

There is no body and the call is idempotent, so it is safe to run repeatedly.

Note

the compile route is PUT .../world. Atlas serves its single-page application for any URL outside the API surface, so a mistyped route returns 200 text/html rather than a 404.

On success you get 200 and a compile result (see "What a successful compile returns"). On failure you get 422 and a body of this shape:

{
  "type": "https://atlas.timebase.io/errors/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "3 violation(s) found in lonestar.",
  "violations": [
    {
      "ruleId": "I-B3",
      "severity": "Error",
      "filePath": "instances/equipment.map.json",
      "jsonPointer": "/3/properties/urn:atlas:ontology:plant:FlowRate",
      "instanceId": "pump-01",
      "message": "Property 'urn:atlas:ontology:plant:FlowRate' is not attached to type 'urn:atlas:ontology:plant:Motor' (instance 'pump-01'): attached to {'urn:atlas:ontology:plant:Pump'}, and 'urn:atlas:ontology:plant:Motor' is neither a declared-attachment type nor a subtype of one. A property may be declared on an instance only when it is attached to the instance's type or one of its supertypes.",
      "suggestion": null
    }
  ]
}

Asking for the list without compiling#

GET /api/v1/branches/{branch}/domains/{domain}/validation
GET /api/v1/branches/{branch}/libraries/{library}/validation

This is the channel you should build your fix-and-recheck loop on. It is cheaper than a compile, it changes nothing, it works on a domain you never wrote to, it survives a page reload, and it is the only place a Warning-severity violation is ever visible.

The response is:

{
  "branch": "main",
  "sha": "7f3d0884",
  "errorCount": 24431,
  "warningCount": 1,
  "blocking": false,
  "violations": [ ... ],
  "groups": [
    {
      "ruleId": "I-B6",
      "severity": "Error",
      "filePath": "instances/equipment.map.json",
      "defIndex": 3,
      "jsonPointer": "/3/properties/urn:atlas:ontology:plant:FlowRate",
      "count": 24428,
      "messages": [ "...", "...", "..." ],
      "suggestion": null
    }
  ],
  "omittedViolationCount": 23931,
  "omittedGroupCount": 0
}
Field What it tells you
branch The branch you asked about.
sha The commit that was actually checked. If you own a draft session that is ahead of the branch, this is your draft's commit and you are seeing your own in-progress work. Everybody else sees the branch. Blank for a library.
errorCount Error-severity violations. This is your to-do list, and it is a true total: it is computed over the whole result, so the caps below can never make a broken domain look cleaner than it is.
warningCount Warning-severity violations. Informational, never blocking. True total, on the same terms.
blocking true when a fatal rule (O-A1 or O-A2) fired. It also means the list is truncated - the checks stopped at a phase boundary, so more problems may be hidden behind it.
groups What to build on. Every violation, collapsed by rule, severity, file and definition, each with a true member count. Ordered biggest problem first, capped at 1,000.
violations A capped sample in pipeline order, for detail and debugging. Capped at 500. Not the full set, and not the actionable account.
omittedViolationCount How many violations the 500 cap left out of violations.
omittedGroupCount How many groups the 1,000 cap left out of groups, applied after ordering, so what you are shown is always the largest problems rather than an arbitrary first slice.

Read groups, not violations#

One broken reference can fan out across every expanded row of an instance map. A field incident produced 97,712 outstanding violations on a single draft from a handful of real mistakes, and reading them as a flat list was neither useful to a person nor kind to the wire.

So the read collapses them. Everything sharing the same rule, severity, file and definition becomes one group carrying a true count, up to three distinct messages, and the first useful suggestion among its members. A violation that belongs to no single definition, such as an ontology-wide one, groups per distinct jsonPointer instead.

Group field What it tells you
ruleId, severity, filePath The same three fields a violation carries.
defIndex The instance-map definition this group anchors to, as its position in that file's array. null for an ontology or whole-domain violation.
jsonPointer The representative pointer, taken from the group's first member. Not every member's.
count The true number of violations in the group, uncapped. Badge on this.
messages Up to three distinct messages, in the order first seen. More members than messages is normal.
suggestion The first "did you mean" hint among the members, if any.

A violation can stand for more than one#

Each entry in violations is field-for-field identical to an entry in the 422 compile body, so you can handle both with the same code. Both now carry one more field:

Field What it tells you
occurrences How many violations this one entry stands for. Almost always 1. Greater than 1 on a summary entry, which is how a rule reports a fan-out it deliberately bounded rather than materialising in full.

Where a rule would otherwise emit thousands of near-identical errors, it now emits at most three real samples per failure site and one weighted summary for the rest. Every total you can read, errorCount, a group's count, the save header and the count in a 422 detail string, sums occurrences rather than counting entries, so the numbers stay honest while the response stays small.

If you total a violation list yourself, sum occurrences. Counting array entries under-reports, and on a fanned-out domain it under-reports by orders of magnitude.

The read is cached#

Repeat reads of the same commit are served from a cache rather than re-running the checks, which makes a fix-and-recheck loop and a polling dashboard both cheap. The cache key includes the commit, so anything you save produces a new commit and therefore a fresh run: you will never be handed a stale verdict for work you just did.

Two things worth knowing:

  • A domain whose instance maps pull from a connector cannot be verified from the commit alone, so its verdict is only held for 60 seconds. A domain with no sourced maps is held for as long as that commit is asked about.
  • Library validation is not cached. Every read runs the checks.

Reading a violation#

Every violation has the same seven fields.

Field What it is
ruleId A stable code such as I-B2 identifying which rule fired. Look it up in the tables below. Safe to grep for in logs.
severity "Error" or "Warning". Almost always "Error".
filePath The file the problem is in, relative to the domain: ontology.jsonld, or instances/<name>.map.json. Empty when the problem is not tied to one file.
jsonPointer An RFC 6901 pointer to the exact spot inside that file, for example /3/properties/urn:atlas:ontology:plant:FlowRate. null when the problem applies to the whole file.
instanceId The id of the offending instance, when the problem is tied to one.
message The full description of the problem, naming the specific type, property or instance.
suggestion A "Did you mean ...?" hint. Usually null. Only O-B1, I-B1, I-B2 and I-B9 produce a spelling suggestion; I-B10 uses the field for advice text. Every other rule leaves it empty.

Tip: filePath and jsonPointer together are the locator pair. They tell you which file to open and where in it to look. They are far more useful than the message text for finding the problem.

Note on quoted messages: the message text in the tables below is reproduced exactly as Atlas emits it, including punctuation, so you can search for it. Values in braces, such as {id}, are filled in at runtime with your own type, property or instance names.


How the checks run#

Validation runs in four ordered phases:

Phase 1: Ontology Format    ->  Is ontology.jsonld valid JSON-LD and well-formed?
Phase 2: Ontology Semantic  ->  Do the types and properties reference each other correctly?
Phase 3: Instance Format    ->  Are the instance map files structurally correct?
Phase 4: Instance Semantic  ->  Do instances use types and properties that actually exist?

Why clearing violations can reveal more#

Validation runs in phases, and three rules decide how far it gets. Each means that clearing what you can see can reveal a further batch.

  1. The fatal halt. O-A1 and O-A2 are the only fatal rules in Atlas. If either fires, checking stops at the end of that phase and phases 2, 3 and 4 never run. Fix these first, always.

  2. The per-file cross-phase skip. If any Phase 3 (instance format) rule fired against an instance map file, every Phase 4 (instance semantic) rule is skipped for that whole file. A file that does not parse cleanly cannot be checked for meaning.

  3. Prerequisite chains. Some rules are silenced when the rule they depend on has already fired at the same spot. The full set:

    If this fired These are skipped at the same spot
    I-B2 (unknown property URI) I-B3, I-B4, I-B9, I-B15, I-B16, I-B17, I-B18, I-B22
    I-B9 (unknown function URI) I-B22
    I-B19 (unresolved cross-node reference) I-B20, I-B21
    I-B20 I-B21
    I-B5 (type is not a relationship) I-B7
    I-B10 (binding path not advertised) I-B11, I-B12, I-B13, I-B14

Tip: treat the violation list as a to-do list you re-fetch after each round of fixes, not as a complete inventory of everything wrong with your model.


Ontology format rules (O-A1 to O-A12)#

These rules check ontology.jsonld itself. They are most likely to fire if you edit the file by hand or if a write was interrupted. filePath is always ontology.jsonld. All are Error severity.

O-A1 and O-A2 are the only fatal rules in Atlas. They stop everything else from running, and they are the only rules that block a save inside a draft session.

Rule Severity When it fires Message you will see How to fix
O-A1 Error (fatal) The file is not parseable JSON at all Ontology file is not parseable JSON at line {L}, column {C}: {parser message} Open the file in a JSON validator and go to the line and column given. Look for a missing closing brace, an unescaped quote, or a trailing comma.
O-A2 Error (fatal) The root is not a JSON object, or @context is missing, or @graph is missing Ontology root must be a JSON object containing '@context' and '@graph'; got {kind}. · Ontology is missing required '@context' at root. · Ontology is missing required '@graph' at root. Both sections are mandatory. Restore them from a previous commit rather than reconstructing them by hand.
O-A3 Error A @graph entry has no @id, or no @type; or the domain's own self-description entry does not declare @type: owl:Ontology Graph entry is missing required '@id'. · Graph entry '{id}' is missing required '@type'. · Domain self-description entry '{id}' must declare '@type': 'owl:Ontology'. Use jsonPointer to find the entry. Add the missing @id or @type. Every entry in @graph needs both.
O-A4 Error An @id is neither an Atlas URN (urn:atlas:ontology: or urn:atlas:vocab:) nor a CURIE whose prefix is declared URI '{id}' does not follow the Atlas convention. Expected '{OntologyBase}{domain}:{LocalName}' or a CURIE whose prefix is declared in '@context'. Either write the full urn:atlas:ontology:{scope}:{LocalName} form, or declare the prefix in @context. The prefixes xsd, rdf, rdfs, owl and atlas are always recognised without declaring them.
O-A5 Error Two @graph entries share the same @id Duplicate '@id' '{id}' — first defined at /@graph/{n}/@id. You have defined the same type or property twice. The message tells you where the first definition is. Remove or rename one.
O-A6 Error A property has no rdfs:range Property '{id}' is missing 'rdfs:range'. owl:DatatypeProperty ranges should be an XSD type (or rdfs:Datatype with owl:unionOf); owl:ObjectProperty ranges should be a class URI. Add a range. Value-carrying properties get an XSD type such as xsd:double; properties that point at another type get that type's URI.
O-A7 Error An owl:Class has no rdfs:subClassOf Class '{id}' is missing 'rdfs:subClassOf'. Every owl:Class must declare a parent (use 'atlas:NodeBase' for top-level domain classes). Give the class a parent. Use atlas:NodeBase if it is a top-level type in your domain.
O-A8 Error A relationship class is missing its atlas:Source or atlas:Target restriction block, or one of them has no owl:allValuesFrom Relationship class '{id}' is missing the 'atlas:Source' owl:Restriction block (with owl:onProperty=atlas:Source and owl:allValuesFrom). and three matching variants for Target and for the missing owl:allValuesFrom Every relationship type needs both endpoint restrictions, and each must name the type it connects with owl:allValuesFrom.
O-A9 Error The kind of range does not match the kind of property Property '{id}' is an owl:DatatypeProperty whose rdfs:range must be an XSD type (e.g. xsd:string, xsd:double) or an rdfs:Datatype with owl:unionOf of XSD types. Got '{range}'. and the matching owl:ObjectProperty variant A value property must range on an XSD type; a property that points at another type must range on a class URI. Change either the property kind or the range so the two agree.
O-A10 Error An @context entry names a library file that is not on disk @context library reference '{ref}' does not resolve to a file on disk (looked at '{abs path}'). Check the library name and that the library is installed on this server.
O-A11 Error A union range lists fewer than two distinct XSD types Property '{id}' declares an owl:unionOf value-type union with fewer than two distinct XSD members. A union must offer a genuine choice (>=2 distinct xsd: types); a single-member union should be a plain single-URI rdfs:range instead. If the property really only takes one type, replace the union with a plain single-URI rdfs:range.
O-A12 Error A property declares rdfs:domain, which Atlas does not read as an attachment Property '{id}' declares 'rdfs:domain' — the retired property-owns-attachment shape. Property↔type attachment now lives on the entity TYPE as an owl:Restriction (attach the property from the type); rdfs:domain on a property definition is no longer read. Remove it and attach the property to the entity type(s) that use it. Delete rdfs:domain from the property, then attach the property from each entity type that uses it. Attachment is now a property of the type, not of the property.

Ask Gage

Ask the reviewer agent: "Check my ontology.jsonld against the O-A format rules (O-A1 through O-A12) and report any violations with their line numbers. I only want format problems right now, not semantic ones."

The reviewer scans the ontology structure without compiling and reports format issues first, which block everything else. Fixing format problems before semantic ones makes your to-do list shorter.


Function save rules (O-F1 to O-F6)#

These are the exception to everything above: they run in a synchronous pre-save validator, not in the compile pipeline. A function save that breaks one of them returns 422 with a violations[] array and persists nothing - it does not land in a draft session the way other ontology saves do.

Rule Severity Trigger Message (verbatim) Fix
O-F1 Error Empty name, or the URI already exists Function name must not be empty. / Function '{name}' already exists (URI '{uri}'). Give the function a name that is unique within the scope. Remember all five ontology kinds share one URN namespace.
O-F2 Error An input or output type does not resolve Input '{name}' type '{type}' does not resolve to a Primitive or Data Type in scope. and the Output variant Use a primitive, or a data type that exists in this domain or an imported library.
O-F3 Error A shared predicate is reused with a different type Input '{name}' reuses shared predicate '{uri}' with type '{type}', but it is already declared with type '{existing}'. Either match the existing type or use a different predicate.
O-F4 Error No output, or an empty body A function must declare exactly one output. / A function must have a non-empty implementation body. Declare exactly one output and provide an implementation body.
O-F5 Error An anchor field is not a datetime field of its type Anchor output field '{field}' is not a datetime field of the output type. and the per-input variant Point the anchor at a datetime field on the relevant type, or remove the anchor.
O-F6 Error An input or output name breaks the name rule. An input name must be a C# identifier (a letter or underscore first, then letters, digits and underscores), must not be a reserved C# keyword, and must not be anchor. An output name can only contain letters, digits, underscores and hyphens. One violation per bad name. Only names that are new or changed on an edit are checked, except an input named anchor on an anchored function, which is refused on every save Input 'flow rate' has an invalid name. An input name can only contain letters, digits and underscores (_), and cannot start with a digit. · Input 'class' has an invalid name: 'class' is a reserved word in C#. Choose another name. · Input 'anchor' has an invalid name: 'anchor' is reserved for the anchor of an anchored function. Choose another name. · Output 'flow rate' has an invalid name. An output name can only contain letters, digits, underscores (_) and hyphens (-). Rename the input or output. Input names become the generated method's parameter names, which is why they follow the C# rules. Contextual keywords such as value and var are allowed.

Ontology semantic rules (O-B1 to O-B4, O-B6 to O-B10, O-B18 to O-B24)#

O-B18 to O-B21 check a property type's default sourcing strategy, and are the ontology twins of the instance-map rules named in each row. They behave like every other O-B rule: they block the write on the default path, and relax to a warning on save inside a draft session. See Property Sourcing Strategies for the feature itself.

These rules check that your types and properties refer to things that exist and form sensible structures. All are Error severity except O-B13, and the unbound-parameter case of O-B19.

There is no O-B5. Referential integrity on delete is covered by Atlas re-validating the whole domain after every write, together with the instance semantic family.

An O-B23 violation under Outstanding items on the Overview: the rule, the message naming the type and property, the ontology file and JSON pointer, and a Show in builder linkAn O-B23 violation under Outstanding items on the Overview: the rule, the message naming the type and property, the ontology file and JSON pointer, and a Show in builder link

An ontology rule as the Overview shows it: O-B23, raised by a type that uses a property whose value is one of two scalars and pins neither.

Rule Severity When it fires Message you will see How to fix
O-B1 Error A cross-reference URI does not resolve anywhere in this ontology, its libraries, or atlas-core. Checked in five places: a property's rdfs:range, a class's rdfs:subClassOf parent, a relationship's atlas:Source/atlas:Target target, a relationship's edge-property, and an entity type's property attachment Property '{uri}' references unresolved URI '{bad}' via {role} — the URI is not declared as an owl:Class, owl:DatatypeProperty, or owl:ObjectProperty in this ontology, its libraries, or atlas-core. · Class '{uri}' references unresolved URI '{parent}' via rdfs:subClassOf ... · Relationship '{uri}' atlas:Source restriction references unresolved URI '{x}' via owl:allValuesFrom. and the Target variant · Relationship '{uri}' edge-property restriction references unresolved property URI '{x}' via owl:onProperty ... · Entity class '{uri}' attachment restriction references unresolved property URI '{x}' via owl:onProperty ... Check spelling and capitalisation of the referenced URI exactly. If suggestion says "Did you mean ...?", it is usually right. Otherwise create the missing type or property, or import the library that defines it. Typical case: an entity type attaches urn:atlas:ontology:plant:FlowRat and the real property is ...:FlowRate.
O-B2 Error A rdfs:subClassOf chain loops back on itself rdfs:subClassOf cycle detected: {A → B → A}. Remove one of the subClassOf links named in the cycle.
O-B3 Error An entity type tries to attach atlas:Source or atlas:Target as if they were ordinary properties Entity class '{node.Uri}' has an attachment restriction whose owl:onProperty is the reserved relationship-endpoint term '{target}'. atlas:Source / atlas:Target declare relationship endpoints and are not valid attachment targets on an entity class — attach a declared Property instead. atlas:Source and atlas:Target belong on relationship types only. Remove the attachment from the entity type and attach a real declared property instead.
O-B4 Error A relationship's Source or Target points at something that is not a node type Relationship '{uri}' atlas:Source restriction targets '{x}', which is not (transitively) a subclass of atlas:NodeBase. Source/Target restriction targets must be node types. and the Target variant Relationships connect node types only. Point Source and Target at classes that inherit from atlas:NodeBase, directly or through a parent.
O-B6 Error A compound field override names a type that the base field does not allow Property '{uri}' restricts field '{path}' to '{type}', which is not a member of the field's declared range '{list}' on compound '{owner}'. You can narrow a compound field only to one of the types the base field already lists. Pick one from the list in the message.
O-B7 Error A compound specialization names a field that the base compound does not have Property '{uri}' specializes field '{path}', which does not resolve to a field of base compound '{base}'. Each owl:onProperty must reference a declared field of the base (intermediate segments of a nested path must be compound-typed fields). Check the field name against the base compound. For a nested path, every step except the last must itself be a compound-typed field.
O-B8 Error A named compound specialization inherits from more than one compound Compound specialization '{uri}' has {n} compound superclasses ({list}); a named compound specialization must have exactly one compound superclass. Give the specialization exactly one compound parent.
O-B9 Error An inline owl:intersectionOf range does not name a known compound type Property '{uri}' has an inline owl:intersectionOf range whose base member does not resolve to a known compound type (atlas:CompoundPropertyType subclass). The intersection must list exactly one class URI that is a known compound type. The intersection must list exactly one class URI, and that class must be a known compound. Without this the property would silently collapse to a single text column, which is why it is an error.
O-B10 Error A child relationship type widens its Source or Target instead of narrowing it Relationship '{uri}' narrows its Source from '{parent}' to '{child}', which is not a subtype of the parent's Source. A child relationship type may only narrow Source/Target to subtypes of the parent's declared types . and the Target variant A specialised relationship may only make its endpoints more specific. Either pick a subtype of the parent's endpoint, or do not inherit from that relationship.
O-B11 Error A relationship type is missing the plain property Atlas derives from it so the connection can be traversed, or that property is malformed. Found in a hand-edited file; a save through the UI or the API always writes it Relationship '{uri}' declares no atlas:traversalProperty. ... and related variants Re-save the relationship type.
O-B12 Error A relationship type's inverseName cannot be used: it equals the type's own name, another relationship type in the same scope already uses it, or another element in the scope already has that name Relationship '{uri}' declares atlas:inverseName '{name}', which ... followed by the reason Choose a different inverseName, or rename the element it collides with.
O-B13 Warning A relationship type reuses the inverseName of one of its ancestors Relationship '{uri}' declares atlas:inverseName '{name}', the same name its ancestor ... Allowed, because the two land at different URIs, but consider a distinct name.
O-B14 Error The inverse direction a relationship type declares is missing, malformed or left behind after inverseName was cleared. Found in a hand-edited file Relationship '{uri}' declares atlas:inverseName ... or ... declares no atlas:inverseName but still carries an atlas:inverseProperty back-pointer ... Re-save the relationship type.
O-B15 Error A property's enumType, or a field binding in fieldEnumOverrides, names something that is not an enumeration type Property '{uri}' binds atlas:enumType '{x}', which resolves to a known class that is NOT an Enumeration Type. ..., or ... references unresolved URI '{x}' via atlas:enumType ... Name an existing enumeration type. For a library enumeration, confirm this scope imports the library.
O-B16 Error A property bound to an enumeration type has a range, or a pinned field type, different from the enumeration's key type Property '{uri}' binds Enumeration Type '{e}', whose atlas:enumKeyType is '{k}', but its rdfs:range is '{r}'. ... Set the property's range to the enumeration's key type, or bind an enumeration whose key type matches.
O-B17 Error An enumeration type is malformed: no key type, an unsupported key type, a key that is not a valid value of the key type, or a duplicate key or member name. Found in a hand-edited file; a save through the API refuses these with 400 Enumeration Type '{uri}' ... followed by the problem Fix the enumeration type, or re-create it through the API.
O-B18 Error A property's default sourcing strategy names a function that does not exist The CalculatedStrategy.functionUri does not resolve to a declared function Check the URI. It is urn:atlas:ontology:{scope}:{Name}. If the function is in a library, confirm this scope imports that library. The instance-map twin is I-B9.
O-B19 Error, and Warning for an unbound parameter A default Calculated strategy binds an argument to a parameter the function does not declare, or leaves a declared parameter unbound The argument's parameter does not resolve against the function's parameters Compare each argument's parameter against the function's parameter predicate URIs. An unbound parameter is a Warning and reads the default for its type, so it appears in GET .../validation rather than in the compile response. The instance-map twin is I-B22.
O-B20 Error A default Calculated strategy reads a property that does not exist The propertyRef argument's propertyUri does not resolve to a declared property Check the URI and, for a library property, that this scope imports the library. Only the property is checked here: a cross-node instanceUri is resolved when the domain compiles, because a library load has no instances to check against.
O-B21 Error A default Static value does not match the property's range The literal does not match the property's declared value type Send a value of the declared type, or change the property's range. BoundStrategy paths are not range-checked. The instance-map twin is I-B4.
O-B22 Error A property's value type is an entity type Property '{p}' has the entity type '{t}' as its value type. An entity is never a property's value: make '{t}' a data type (its attached properties become fields) or give '{p}' a primitive or data-type value type. Make the type a data type, so its attached properties become fields, or give the property a primitive or data-type value type. There is no automatic migration: fix the ontology. To source the parts of a value independently, use a data type with the Composed strategy.
O-B23 Error A concrete entity or relationship type uses a property with a leaf that is still open after the type's own pins and its ancestors' pins. A property has an open leaf when its own value type is a union of scalars, or when its data type has a union-typed field (such as TVQ.Value) that the property has not pinned. Abstract types are skipped, because only their concrete subtypes compile. It is reported once per type and property, for library types too Type '{t}' uses property '{p}' with an unresolved type (Value is one of double, string, boolean), so '{t}' cannot compile. Pin it on '{t}' or on '{p}'. When a library is involved the message adds '{p}' comes from library '{lib}'; pin it on '{t}' here, or in '{lib}'. for a domain type that uses a library property, '{p}' comes from library '{lib}'; pin it there. when the type is in another library, Both come from library '{lib}'; fix it there. when the property and the type are from the same library, or '{t}' comes from library '{lib}'; fix it there. when only the type is Pin the leaf in one of two places. Pin it on the property when the concept has one shape everywhere: under Value type in the Property drawer, use the Pin fields table to set Value to xsd:double, xsd:string or xsd:boolean. Pin it on the type when the shape depends on the type: open the type's drawer, go to the Pins tab and choose a member for the open leaf. A subtype can pin a leaf its ancestors left open. It is a normal Error, not fatal: the domain still loads and every save still lands, but compile refuses until every leaf used by a type is pinned. A domain can pin a library property on its own type without editing the library. A domain reports it for its imported libraries too, so the fix is named even when the domain itself only holds instances.
O-B24 Error A pin on a type is not usable. It is found in a file, because a save through the UI or the API refuses these cases up front with the same words. One violation is reported for each bad pin and path Type '{t}' pins property '{p}', which it does not use. / Type '{t}' pins the value type of '{p}', whose type is not a union. / Type '{t}' pins '{p}.{path}', which does not resolve to a field of base compound '{base}'. / Type '{t}' pins '{p}.{path}' to '{type}', which is not a member of the field's declared range '{list}' on compound '{owner}'. / Type '{t}' pins '{p}.{path}', which property '{p}' already fixes to '{type}'. A property pin is final; remove the type pin. / Type '{t}' pins '{p}.{path}', which its ancestor '{a}' already fixes to '{type}'. A subtype only pins a leaf its ancestors left open. Remove the pin, or correct it. A type pins only a property that it attaches itself or inherits from an ancestor. The pinned type must be one of the members the leaf allows. A leaf that the property already fixed, or that an ancestor type already pinned, cannot be pinned again. A pin that binds an enumeration type is also checked here: the enumeration must exist and its key type must equal the pinned type.

Instance format rules (I-A1 to I-A11)#

These rules check that your instance map files can be read. filePath is instances/{name}.map.json. All are Error severity.

Rule Severity When it fires Message you will see How to fix
I-A1 Error The file is not parseable JSON, or its top level is not an array Instance map file is not parseable JSON at line {L}, column {C}: {parser message} · Instance map file root must be a JSON array; got {kind}. Run the file through a JSON validator. An instance map file must be a JSON array of entries, even if it contains only one.
I-A2 Error An entry is missing id, type or instanceId Instance map entry is missing required field '{field}'. All three are mandatory on every entry.
I-A3 Error The same id appears more than once anywhere in the domain, in the same file or across different files Instance id '{id}' is not globally unique across the domain; it appears in {file list}. Every instance map entry must have a unique id across all *.map.json files. Instance ids are unique across the whole domain, not just per file. Rename one of the duplicates. The message lists every file the id appears in.
I-A4 Error A property key does not start with urn:atlas:ontology: Property key '{key}' is not a full URI. Property keys must start with 'urn:atlas:ontology:' (short names are silently dropped at compile). Use the full URI from the ontology, for example urn:atlas:ontology:plant:FlowRate. Short names are not resolved.
I-A5 Error A strategy type is not one of the six recognised values Strategy type '{t}' is not recognised. Allowed values: StaticStrategy, BoundStrategy, CalculatedStrategy, NullStrategy, CompositeStrategy, ReferenceStrategy. Use one of the six exactly as spelled, including capitalisation. There are six, not three.
I-A6 Error A strategy is missing a field its type requires {strategyType} requires field '{field}'. Required fields are: StaticStrategy needs value; BoundStrategy needs bindingPath; CalculatedStrategy needs both functionUri and arguments.
I-A7 Error A map[] entry has no name, or no data, or its data is not in a recognised form Map entry is missing required field 'name'. · Map entry is missing required field 'data'. · Map entry 'data' value '{d}' does not parse as a count-encoded value. Expected one of: single value, CSV list, range (e.g. '1-10' or '1-10/2'), or multiplier (e.g. 'A*5, B*3'). Use one of the four accepted forms from the message: a single value, a comma-separated list, a range such as 1-10 or 1-10/2, or a multiplier such as A*5, B*3.
I-A8 Error A template placeholder does not match any map[] entry name, or a map[] entry name is not used anywhere instanceId template references '[{v}]' but no map[] entry declares name '{v}'. · label template references '[{v}]' ... · {field} template references '[{v}]' ... · map[] entry name '{n}' is an orphan — no instanceId, label, property strategy, or relationship template references it. Placeholders use square brackets, [Num], not curly braces. The name inside must match a map[] entry's name exactly. The rule works both ways: it also fires if you delete a placeholder and leave the map[] entry behind, so remove the unused entry too.
I-A9 Error A relationship instance map declares source but not target, or the other way round A relationship instance map must declare both 'source' and 'target'; '{missing}' is missing. Endpoints come as a pair. Add the missing one. A missing type is reported by I-A2, and a type that is not a relationship is reported by I-B5.
I-A10 Error A transform's regex is not a valid expression transforms.{source}.{column} regex syntax error: {ex.Message} Fix the expression. The message carries the underlying regular expression error.
I-A11 Error In RowsetTemplate mode, a map[].data is not a valid [source.column] reference map[{i}].data must reference a source column via [source.column] syntax in RowsetTemplate mode (was: '{d}'). · map[{i}].data parse error: {ex.Message} In RowsetTemplate mode every map[].data must reference a connector column in the form [sourceName.columnName].

Instance semantic rules (I-B1 to I-B23)#

These rules check that a structurally valid instance map actually makes sense against your ontology and your connectors. All are Error severity except the second form of I-B22.

Remember the cross-phase skip: if any I-A rule fired against a file, none of these rules run against that file at all.

An I-B10 violation under Outstanding items on the Overview: the rule, the binding path no connector advertises, the advice text, the instance map file and JSON pointer, and a Show in builder linkAn I-B10 violation under Outstanding items on the Overview: the rule, the binding path no connector advertises, the advice text, the instance map file and JSON pointer, and a Show in builder link

An instance rule as the Overview shows it: I-B10, raised by a source whose binding path no connector advertises. The advice line is the rule's suggestion field.

Rule Severity When it fires Message you will see How to fix
I-B1 Error An instance's type is not a type in your ontology Instance type '{t}' is not registered in the ontology as an owl:Class subClassOf atlas:NodeBase. plus a Did you mean '{x}'? suggestion when a near match exists Check spelling and capitalisation against the ontology. If you added the type recently, confirm the ontology was saved and published.
I-B2 Error A property URI used in the instance map is not in the ontology Property URI '{uri}' is not registered in the ontology as owl:DatatypeProperty or owl:ObjectProperty. plus a suggestion Check the URI exactly. This rule silences eight other checks on the same property, so expect new violations after you fix it.
I-B3 Error The property exists, but it is not attached to this instance's type or any of its parent types Property '{propUri}' is not attached to type '{def.Type}' (instance '{def.Id}'): {attachedLabel}, and '{def.Type}' is neither a declared-attachment type nor a subtype of one. A property may be declared on an instance only when it is attached to the instance's type or one of its supertypes. Either change the instance's type, or attach the property to that type in the ontology. Attaching it to a parent type also satisfies the rule, because attachment is inherited.
I-B4 Error A StaticStrategy value is the wrong data type for the property The message names the property, the value and the expected type from the property's rdfs:range. For a data-type property the literal is checked field by field, and the message names the dotted field, for example Data.Value. Pinned field types are honoured, and a blank field means not set Change the literal to match the property's declared range. A property with range xsd:double cannot take "fast".
I-B5 Error A map declares source/target but its type is not a relationship type Instance map '{id}' declares a source/target endpoint but its type '{t}' is not a relationship archetype (a class subClassOf RelationshipBase or ContainsBase). plus a suggestion Point type at a relationship type, or remove the endpoints if you meant to author an entity.
I-B6 Error A relationship endpoint does not match any instance in the expanded domain The message comes from the endpoint resolver and names the unresolved endpoint Check the endpoint template against the instance ids it should produce. Remember that expansion happens first, so the endpoint must match an id after expansion.
I-B7 Error An endpoint resolves, but the instance it resolves to is the wrong type for this relationship The message comes from the endpoint resolver and names the type mismatch Either point at an instance of the right type, or widen the relationship's Source/Target in the ontology. Silenced when I-B5 fired.
I-B8 Error Containment relationships form a loop, including an instance containing itself Detected a containment cycle: {a → b → a}. atlas:contains-based relationships must form an acyclic graph. Containment must be a tree. Break the loop named in the message.
I-B9 Error A CalculatedStrategy names a function that is not registered CalculatedStrategy on property '{uri}' references function URI '{f}', which is not registered in the FunctionRegistry. plus a suggestion Check the function URI. If you added the function recently, confirm it was saved and the domain recompiled.
I-B10 Error A source's bindingPath is not advertised by any connector sources.{n}.bindingPath '{p}' is not advertised by any connector. with suggestion Check that the connector is enabled and its descriptor advertises this binding path or matches a template. Confirm the connector is enabled and online, then check the path. Binding paths are slash-delimited starting from the connector instance id and never use a scheme:// prefix.
I-B11 Error A transform targets a column the connector does not emit transforms.{s}.{c}: column '{c}' not present in rowset emitted by '{path}'. Available columns: {list}. Pick one of the columns listed in the message.
I-B12 Error A named capture group has the same name as a connector column transforms.{s}.{c} capture group '{n}' collides with column '{n}' emitted by connector at binding path '{p}'. Rename the capture group or remove the conflicting transform. Rename the capture group.
I-B13 Error Two transforms on the same source produce the same capture name transforms.{s}.{a} and transforms.{s}.{b} both produce a capture group named '{n}'. Rename one of them. Rename one of the two capture groups.
I-B14 Error A map[].data reference names a source or column that does not exist map[{i}].data references undeclared source '[{s}.{c}]'. Available sources: {list}. · map[{i}].data references column '[{s}.{c}]' but '{c}' is not a connector-emitted column or a declared capture on source '{s}'. Available: {list}. Use one of the sources or columns listed in the message.
I-B15 Error A Composed (CompositeStrategy) member key is not a field of the property's data type Composite member '{m}' is not a field of data type '{r}'. Use a field that the data type actually declares.
I-B16 Error The Composed strategy is used on a value that is not a data type The Composed strategy needs a data-type value; '{p}' is a scalar, or a one-of union, or unranged. The same rule fires for a nested Composed strategy on a primitive field Give the property a data type as its value type, or use Static, Bound or Calculated for the whole value.
I-B17 Error A property or member has more than one CompositeStrategy entry Property '{uri}' has {n} CompositeStrategy entries; at most one is allowed (v1 supports no temporal mode-switching — members may still carry their own validFrom intervals). Keep one CompositeStrategy entry. If you need time-varying behaviour, put the validFrom intervals on the individual members instead.
I-B18 Error Two entries in a member's strategy list start at the same validFrom CompositeStrategy member '{m}' has overlapping strategy intervals: more than one entry shares validFrom {t}. Each interval must start at a distinct validFrom. Give each interval a distinct start time.
I-B19 Error A cross-node reference names an instance that does not exist CalculatedStrategy on property '{uri}' has a cross-node propertyRef whose instanceUri '{x}' does not match any expanded instance id in the domain. The reference resolves to null at runtime (no recompute routing is built and the input reads null). and a matching ReferenceStrategy variant Check the instance id after expansion. This is an error rather than a silent null because the reference would never recompute.
I-B20 Error A cross-node reference names a property that the target instance's type does not have CalculatedStrategy on property '{uri}' reads a cross-node input from instance '{x}', but {reason}. and a matching ReferenceStrategy variant Attach the property to the target's type, or point at a different property. Silenced when I-B19 fired.
I-B21 Error A ReferenceStrategy aliases a property whose type does not fit ReferenceStrategy on property '{uri}' (range '{a}') aliases target property '{t}' whose range is '{b}'; the target's range must be assignable to the declaring property's range. The aliased property's range must be compatible with the declaring property's range. Change one of them.
I-B22 Error A calculation argument's parameter does not resolve to a declared parameter of the function, or is ambiguous CalculatedStrategy on property '{uri}' binds argument parameter '{p}', which does not resolve to any declared parameter of function '{f}'. Accepted forms: the bare parameter name or its full predicate URI. Declared parameters: {list}. plus an ambiguous variant Use one of the parameter names listed in the message, either the bare name or its full predicate URI.
I-B22 Warning A declared function parameter is left unbound CalculatedStrategy on property '{uri}' leaves function parameter '{predicateUri}' (name '{name}') unbound; it defaults to null/0 at runtime, which commonly makes the calculated value 0. Bind the missing parameter, or accept the default. See "The warnings" below.
I-B23 Warning An instance's type is marked abstract. Abstract types are derivation-only, so this reports a model that has drifted: the type was marked abstract after these instances already existed Anchored on the instance's type Re-type the instance onto a concrete subtype, or unmark the type as abstract. Creating or re-typing an instance onto an abstract type is refused outright by the write path, so this rule only ever describes instances that predate the marker. It fires alongside I-B1, never instead of it

Connector-bound rules (I-A10, I-A11, I-B10 to I-B14)#

These seven rules only exist when instance maps pull their rows from a connector. They have one behaviour worth knowing about:

  • With no connector context available, the I-B10 to I-B14 family does not run, so no violations from it are reported.
  • With connectors registered but no external data store wired in, I-B10 runs and I-B11 to I-B14 do not.

The practical effect: the same domain can validate cleanly on one machine and produce a wall of I-B10 violations on another, purely because of what connectors are available. If a connector-bound domain validates surprisingly cleanly, check that your connectors are actually connected before you conclude the model is correct.

The warnings#

Atlas has four Warning-severity violations: the unbound-parameter cases of O-B19 and I-B22, I-B23's abstract-type case, and O-B13's reused inverse name. Because a compile only fails on Error severity, a domain whose only problem is a Warning compiles successfully and returns a normal 200 compile result. That result has no field that can carry a warning, so the warning is invisible on the compile path entirely.

The only way to see a Warning is GET .../validation. If your calculated values are unexpectedly coming out as zero and your compile is clean, this is the first thing to check.


What a successful compile returns#

A compile that passes validation returns 200 and this object:

{
  "entityCount": 634,
  "edgeCount": 73,
  "archetypeCount": 42,
  "compiledAt": "2026-08-03T09:14:22.881Z",
  "functionErrors": []
}
Field What it tells you
entityCount The number of entities in the compiled world. This is not your instance count. Atlas creates an entity for each instance and one for every property-strategy occurrence, so this number is always considerably larger than the number of instances in your maps. Use it as a relative trend between compiles, not as a count of anything you authored.
edgeCount The number of relationship edges between instances.
archetypeCount One archetype per entity type, plus one per relationship type, plus a small fixed number for the strategy kinds. It is a count of your type definitions and does not vary with your instance data at all. Compare it against the number of types you declared, never against your instance count.
compiledAt The UTC timestamp of this compile run.
functionErrors Calculation functions that failed to compile. See below.

GET .../world returns the same counts plus three more fields for the cached world:

Field What it tells you
isStale true when the domain's files or an imported library have changed since this world was compiled. Recompile to clear it.
compiledAtSha The commit this world was compiled from.
currentSha The branch's current commit.

There is no warnings field on either response. If you are looking for per-item diagnostics in a successful compile, functionErrors is the only thing there.

functionErrors#

functionErrors is the compile's one soft-failure channel: a calculation function whose expression failed to compile is reported here, and the compile still succeeds. These entries do not have rule IDs and do not come from the validation pipeline, so you will not find them in the tables above and they never appear in a 422 body or in GET .../validation.

A function in this list is not calculating. Everything else in the domain still compiled.


Fixing violations#

  1. Get the current list: GET /api/v1/branches/{branch}/domains/{domain}/validation
  2. If blocking is true, fix the O-A1 or O-A2 violation first and fetch the list again. Nothing else was checked.
  3. Fix the violations, using filePath and jsonPointer to find each one.
  4. Save the ontology or instance map, through the browser interface or the API. Check the response for the Atlas-Validation-Violations header. If it is present, something is still outstanding.
  5. Fetch the list again. Fixing a violation can reveal violations that were previously hidden behind it.
  6. When the list is clean, publish your draft: POST /api/v1/branches/{branch}/draft/publish
  7. Compile: PUT /api/v1/branches/{branch}/domains/{domain}/world

Compile is idempotent and safe to run repeatedly. If a compile fails, the previously compiled world keeps serving queries unchanged.


Troubleshooting#

  • My save returned success but nothing changed on the branch. A save with outstanding violations goes into your private draft session and does not advance the branch. Check the response for an Atlas-Validation-Violations header, fetch GET .../validation to see what is outstanding, fix it, then publish the draft with POST /api/v1/branches/{branch}/draft/publish.

  • I saved and compiled, both returned success, and my queries still show the old data. While you are working inside a draft session, a compile builds your own copy and leaves the world that everyone else queries in place. Publish the draft, then compile again.

  • Where do I read warnings from? On a successful compile, the diagnostics are in functionErrors. On a compile with outstanding violations you get HTTP 422 and an array called violations. On a save you get a count in the Atlas-Validation-Violations response header, and the full list from GET .../validation.

  • I fixed an error and none of my model appeared. Compile is all or nothing. Validation runs before anything is built, so a single Error means no world at all is produced. Nothing appears until the last Error is gone. Types and instances are never partially excluded.

  • My compile call returns an HTML page instead of JSON. You are calling the retired POST .../compile route. Retired routes do not return 404 in Atlas: the single-page application shell is served instead, so you get 200 text/html. Use PUT /api/v1/branches/{branch}/domains/{domain}/world.

  • The violation list is shorter than the number of problems I know about. Three things hide violations: a fatal O-A1/O-A2 stops all later checks (blocking will be true); an instance format error on a file silences every semantic check on that whole file; and prerequisite rules silence dependent rules at the same spot. Re-fetch the list after every round of fixes.

  • My calculated property is always 0 and the compile is clean. You have most likely left a function parameter unbound. That is the I-B22 Warning, and a Warning can never appear in a compile response. Call GET .../validation and look at warningCount.

  • My placeholder is not expanding and I only get one instance. Instance map placeholders use square brackets, [Num], not curly braces. A {Num} is treated as literal text, not a template, which is why nothing expands. Rule I-A8 reports the mismatched cases.

  • I get I-B3 even though the property is in my ontology. The property exists (otherwise you would get I-B2) but is not attached to the instance's type. Attachment now lives on the entity type as a restriction, not on the property as rdfs:domain. If your ontology still uses rdfs:domain you will also be seeing O-A12. Remove rdfs:domain and attach the property from the type.

  • The same domain validates cleanly on my machine but reports a wall of I-B10 errors on the server. The connector-bound rules do nothing when there is no connector context. A clean result on a machine with no connectors does not mean the binding paths are correct.

  • entityCount is much larger than my instance count. That is correct. entityCount includes one entity for every property-strategy occurrence as well as one per instance. It is not a count of your instances and was never meant to be used as one.


Checking a domain against this reference#

Ask Gage

Ask the reviewer agent: "Scan my {domain-name} domain for any violations of the O-B (ontology semantic) and I-B (instance semantic) rules. Report each one with its rule ID, the affected type or property, and what the message says - but do not attempt to fix it."

The reviewer reads your model and cross-checks it against the rule families you just read about. It reports violations clearly without modifying anything, giving you a to-do list to work through.


esc
↑↓ navigate · ↵ open · ⌘K anywhere