Skip to main content

Validation Checks

This is the canonical catalogue of the diagnostic codes CIMVocabCheck emits. The CLI, language server, and editor integrations all surface these same codes (a stable SparqlValidationCode enum).

Each finding carries a severity (ERROR, WARN, INFO), a code, a human-readable message, and — where resolvable — a line/column and the offending term. How severities map to pass/fail is controlled by strictness; the severity of an individual code can be overridden (or the code switched off) with rules.

All codes at a glance​

CodeSeverityCategoryTriggers when
SYNTAX_ERRORERRORSyntaxThe document is not syntactically valid SPARQL or Turtle
UNKNOWN_CLASSERRORExistenceClass IRI not found in the selected profiles
UNKNOWN_PROPERTYERRORExistenceProperty IRI not found in the selected profiles
UNKNOWN_VOCABULARY_TERMERRORExistenceTypo in a closed standard vocabulary (rdf/rdfs/owl/sh)
GRAPH_NOT_CONFIGUREDWARNScopeGRAPH <g> used but <g> has no mapped profiles (named-graph scope only)
UNSUPPORTED_DYNAMIC_PROPERTYWARNScopeVariable predicate / type-object that cannot be statically resolved
PROPERTY_NOT_ALLOWED_FOR_CLASSERRORSemanticSubject's type is not a subclass of any rdfs:domain of the property
QUERY_IMPLIED_TYPEINFOSemanticSubject has no rdf:type but the property implies exactly one domain
DATATYPE_MISMATCHWARNSemanticLiteral object's datatype is incompatible with rdfs:range
PROPERTY_MAY_BE_ABSENTINFOSemanticA property whose CIM multiplicity allows zero values is matched outside OPTIONAL
INVALID_ENUM_VALUEERRORSemanticObject IRI is not a member of the property's enumeration rdfs:range
UNKNOWN_TERM_IN_EXPRESSIONWARNExistenceA constant IRI in a FILTER/VALUES/BIND expression is unknown to every index (class/property/enumeration member)
NODE_KIND_INCOMPATIBLE_WITH_RANGEWARNSHACLsh:nodeKind conflicts with the property's rdfs:range
DATATYPE_INCOMPATIBLE_WITH_RANGEWARNSHACLsh:datatype used on an object property (range is a class)
CLASS_INCOMPATIBLE_WITH_RANGEWARNSHACLsh:class used on a datatype property (range is a literal type)
INVALID_CARDINALITYERRORSHACLsh:minCount exceeds sh:maxCount on the same property shape
INVALID_VALUE_RANGEERRORSHACLA value-range constraint is self-contradictory — a lower bound (sh:minInclusive/sh:minExclusive) exceeds an upper bound (sh:maxInclusive/sh:maxExclusive)
CARDINALITY_INCOMPATIBLE_WITH_MULTIPLICITYWARNSHACLsh:minCount/sh:maxCount cannot be satisfied given the property's declared CIM cims:multiplicity
PROJECTED_VARIABLE_UNBOUNDWARNVariablesA SELECT/DESCRIBE/CONSTRUCT-template variable appears nowhere in the query body
UNUSED_VARIABLEWARNVariablesA variable bound in a triple pattern, BIND, or VALUES is never used anywhere else
The "exists in another profile" hint

When a class or property does not exist in the selected profiles but does exist in another loaded profile, the UNKNOWN_CLASS / UNKNOWN_PROPERTY annotation carries a foundInOtherProfiles list naming where it lives — a hint that you may have the wrong profile in scope rather than a real typo. Editors surface this as a hint on the underline. This is exactly what fires when a term is used in the wrong named graph under per-graph scoping — see Endpoints → per-graph validation and namedGraphs.

Existence checks​

Every class and property IRI in the query or shapes graph is looked up in the schema index. These fire regardless of how completely the schema annotates semantics:

  • UNKNOWN_CLASS / UNKNOWN_PROPERTY — the IRI is not declared by any selected profile.
  • UNKNOWN_VOCABULARY_TERM — a term in a closed standard vocabulary (rdf, rdfs, owl, sh) that the official W3C vocabulary does not define, e.g. rdf:typ, owl:Clas, sh:minCountt. This is checked in every position a term can appear — predicate, rdf:type object, and other object positions such as sh:nodeKind sh:IRII or sh:severity sh:Violatio. The XSD datatype namespace is likewise a closed set, so a typo'd sh:datatype xsd:strng is reported here too (valid XSD 1.1 datatypes and rdf:langString are accepted). Controlled by standardVocabulary.
  • UNKNOWN_TERM_IN_EXPRESSION (WARN) — a constant IRI used in a FILTER, VALUES, or BIND expression that is unknown to every schema index (class, property, and enumeration member). It is a warning, not an error, because such a constant can legitimately be an instance IRI the schema does not track. Curated CGMES header terms (rdf:Statements, …) and open-namespace terms are accepted.

Header extension terms. The IEC 61970-552 / 600-2 model header coins a handful of terms directly under the closed rdf: namespace (rdf:Statements, rdf:Statements.subject, rdf:Statements.predicate, rdf:Statements.object). These are recognised as a curated header vocabulary, so their use in sh:path, sh:in, and expressions is accepted rather than flagged as a closed-namespace typo.

Enumeration members. CGMES enumeration values — individuals typed by an enumeration class, e.g. cim:WindGenUnitKind.offshore — are indexed as a distinct kind of term. Used correctly in object position (?u cim:WindGeneratingUnit.windGenUnitType cim:WindGenUnitKind.offshore) they are recognised, not flagged. Using one where a class is expected (after rdf:type) or as a predicate is reported with a message that names it as an enumeration value rather than a missing class/property. A wrong value in object position — a typo, or a member of a different enumeration — is reported as INVALID_ENUM_VALUE (see Semantic checks).

Semantic checks​

When the schema index carries rdfs:domain, rdfs:range, or rdfs:subClassOf (as loaded from real CGMES RDFS files), these additional checks run:

  • PROPERTY_NOT_ALLOWED_FOR_CLASS — the subject's rdf:type is not a subclass of any rdfs:domain of the property; also fires when adjacent path-chain segments have disjoint range/domain sets.

  • QUERY_IMPLIED_TYPE (INFO) — the subject carries no explicit rdf:type but the property has exactly one domain, so the type is implied.

  • DATATYPE_MISMATCH — a literal object's datatype is incompatible with the property's rdfs:range.

  • INVALID_ENUM_VALUE — a URI object used for a property whose rdfs:range is an enumeration is not one of that enumeration's members (e.g. a misspelt cim:WindGenUnitKind.offshroe). Fires only when every declared range is an enumeration with members in scope, so instance IRIs of ordinary object properties are never flagged. The same check applies to enumeration values compared against a variable in a FILTER(?kind = cim:…) / FILTER(?kind IN (…)) expression or supplied via a VALUES row, when the variable's property context is a known enumeration.

  • PROPERTY_MAY_BE_ABSENT (INFO) — a property whose CIM cims:multiplicity allows zero values (0..1, 0..n) is matched in the query's mandatory clause. Every triple pattern outside OPTIONAL is an inner join, so such a pattern silently removes each solution whose subject does not carry that optional attribute — the classic reason a CGMES query returns fewer rows than expected. See below for when it fires.

rdfs:subClassOf traversal is transitive and cycle-safe across the union of all profiles in scope.

Unused-variable checks​

Two schema-independent warnings catch variables that are declared but never meaningfully used — typically a typo (?nmae vs ?name) or a leftover from a query edit. Because they need no schema, they also fire in the syntax-only fallback (no config, unreachable endpoint):

  • PROJECTED_VARIABLE_UNBOUND — a variable in the result surface (an explicit SELECT list, a CONSTRUCT template, a DESCRIBE list) that does not appear anywhere in the query body (WHERE / BIND / VALUES). It is unbound in every result row.
  • UNUSED_VARIABLE — a variable bound in a triple pattern, BIND, or VALUES that occurs exactly once in its scope: never projected, filtered on, ordered/grouped by, or otherwise reused. A deliberately unused pattern variable is idiomatically written as a blank node ([]).

The check stays quiet on idiomatic SPARQL — no warning is raised for:

  • variables under SELECT * / DESCRIBE * (everything is projected), or anywhere in an ASK body (a pure existence test);
  • single-use variables inside EXISTS / NOT EXISTS / MINUS bodies (existence tests too), although occurrences there do count as uses of outer variables;
  • variable predicates (?s ?p ?o), GRAPH ?g / SERVICE ?s names, and sub-SELECT projections joined into the outer scope — none of which can be replaced by a blank node;
  • SHACL pre-bound variables in embedded constraint SPARQL ($this, ?value, $PATH, $shapesGraph, $currentShape), which the SHACL engine binds before evaluation.

Each SELECT scope is analyzed independently, mirroring SPARQL's sub-query semantics. SPARQL Update requests are not checked. Both findings are WARN, so strictness permissive suppresses them and strict turns them into errors.

Lenience policy — silent when the schema is silent

A semantic check is skipped when the schema doesn't carry the information it needs: no rdfs:domain → no domain/implied-type check; no rdfs:range (or a class range) → no datatype check; inverse/alternative/repetition path operators → path-chain check skipped for that segment. See Known limitations for the full policy.

Optional-property hints​

Roughly two thirds of the properties in a CGMES profile declare a lower bound of zero, and requiring one is frequently deliberate ("only the units that declare a governor"). So PROPERTY_MAY_BE_ABSENT is a hint, never an error, and it is raised only where wrapping the pattern in OPTIONAL would be a meaningful edit — that is, when all of the following hold:

  • The triple is matched, not produced: CONSTRUCT and INSERT/DELETE template triples are skipped.
  • The triple is in the mandatory root clause. A triple already inside OPTIONAL, a UNION branch, MINUS, or EXISTS/NOT EXISTS sits in a scope the author chose.
  • The object is a plain variable. A constant or literal object is a deliberate value filter, where requiring the property to be present is the point.
  • That object variable is not otherwise constrained by a FILTER, BIND, or VALUES — same reasoning: the query already depends on the value existing.
  • The subject is bound elsewhere in the mandatory clause, or is a constant IRI. If the pattern is the only thing binding the subject, making it optional would leave nothing to join against.

One hint is emitted per attribute per subject. As everywhere else, a schema that declares no cims:multiplicity for the property produces no finding.

SELECT ?line ?name ?description WHERE {
?line a cim:ACLineSegment ;
cim:IdentifiedObject.name ?name ; # 1..1 — quiet
cim:IdentifiedObject.description ?description . # 0..1 — PROPERTY_MAY_BE_ABSENT
}

Switch it off for a project with "rules": { "PROPERTY_MAY_BE_ABSENT": "off" } — see Configuration → rules.

SHACL property-shape checks​

Two passes run over a SHACL shapes graph (see Known limitations for the full breakdown). On every property shape (any blank node with sh:path):

  • NODE_KIND_INCOMPATIBLE_WITH_RANGE — sh:nodeKind forces a non-literal but rdfs:range is a datatype (or vice versa).
  • DATATYPE_INCOMPATIBLE_WITH_RANGE — sh:datatype on a property whose range is a class.
  • CLASS_INCOMPATIBLE_WITH_RANGE — sh:class on a property whose range is a literal datatype.
  • INVALID_CARDINALITY — sh:minCount exceeds sh:maxCount.
  • CARDINALITY_INCOMPATIBLE_WITH_MULTIPLICITY (WARN) — the shape's sh:minCount/sh:maxCount cannot be satisfied given the property's declared CIM cims:multiplicity: a sh:minCount above the schema's upper bound, or a sh:maxCount below its lower bound. A shape that is merely stricter than the schema (the usual profile-tightening pattern) is accepted — only genuine contradictions are flagged.

Value constraints​

Beyond the range checks, several value-level SHACL constraints are validated:

  • sh:in — every URI member of the value list is checked. When the property's range is a known enumeration, a member that is not one of its values is reported as INVALID_ENUM_VALUE; otherwise a member that is unknown to every index (class, property, enumeration member) is flagged as a missing term. Literal members are skipped.
  • sh:hasValue — when the property's range is a known enumeration, a required value that is not one of its members is reported as INVALID_ENUM_VALUE.
  • sh:datatype — a value in the XSD namespace that is not a valid XSD 1.1 datatype is reported as UNKNOWN_VOCABULARY_TERM (e.g. xsd:strng).
  • Value ranges — sh:minInclusive / sh:minExclusive / sh:maxInclusive / sh:maxExclusive are checked for a self-contradiction: a lower bound above an upper bound (and, when either bound is exclusive, a lower bound equal to an upper bound, which admits no value) → INVALID_VALUE_RANGE.

Target and property-reference constraints​

Predicates whose value names a CIM property by IRI are existence-checked, reporting UNKNOWN_PROPERTY for an unknown term (the common rdf:type and other standard/header terms are accepted):

  • sh:targetSubjectsOf / sh:targetObjectsOf — the target property must exist.
  • sh:equals / sh:disjoint / sh:lessThan / sh:lessThanOrEquals — the paired property must exist.
  • sh:ignoredProperties — every property in the list (used with sh:closed) must exist.

Deactivated shapes​

A shape carrying sh:deactivated true is skipped entirely — none of the checks above run on it.

Shape-structure terms​

Shape-structure findings (sh:targetClass, sh:class, sh:path against the schema) reuse the existence codes above. Three kinds of term are not checked against the CIM schema, so they are never falsely reported as unknown: standard-vocabulary classes (e.g. rdf:List, rdfs:Resource), terms the shapes file declares itself (any URI subject of rdf:type, rdfs:subClassOf, rdfs:subPropertyOf, rdfs:domain, or rdfs:range), and the sh:path / sh:class of a custom constraint component's parameters or validators (sh:parameter, sh:nodeValidator, sh:propertyValidator) — which declare a parameter rather than reference a CIM term. A closed-namespace typo in class position (e.g. sh:Fooo) or in a sh:path (e.g. sh:path rdf:typ) is still reported, as UNKNOWN_VOCABULARY_TERM. Embedded SPARQL (sh:sparql, sh:target, sh:validator, sh:rule) is extracted and validated with the full SPARQL check set; its findings are mapped back to positions in the Turtle source.

See also​