Skip to content

Diagnostics

Every error, not the first

Building a schema returns all the diagnostics. Someone repairing a forty-file import graph needs the list; giving them one error, then another after the next five-second build, is a way of turning a ten-minute job into an afternoon.

error[XSD1201]: no type named `{urn:example}Missing`
  --> schemas/report.xsd:14
  help: check the spelling, or add an xs:import for its namespace

Four parts, and each one is addressable:

Part Field
error severity error, warning or note
XSD1201 code stable, greppable, safe to match on
the message message what is wrong
--> file:14 spans where; there may be more than one
help: help what to do about it
import xsdkit

schemas, diagnostics = xsdkit.load("report.xsd", conformance="lax")

for d in diagnostics:
    print(d.code, d.severity, d.message)
    for span in d.spans:
        print("   ", span.uri, span.line, span.label)
    if d.help:
        print("   help:", d.help)

errors = [d for d in diagnostics if d.is_error]
use xsdkit::{Compilation, Conformance, SchemaSetBuilder};

let Compilation { schemas, diagnostics } = SchemaSetBuilder::new()
    .conformance(Conformance::Lax)
    .file("report.xsd")
    .compile();

for d in diagnostics.iter() {
    println!("{d}");
}

Match on code, never on the message text. Codes are part of the compatibility promise; wording is not.

Getting them, or not

Two shapes, because two situations.

Schema is expected to be sound Schema is expected to be imperfect
Python SchemaSet.from_file(...) raises SchemaError xsdkit.load(...) returns (schemas, diagnostics)
Rust compile().into_result() → Result<Schemas, Diagnostics> compile() → Compilation { schemas, diagnostics }

SchemaError carries the whole list, so the raising form loses nothing:

try:
    schemas = xsdkit.SchemaSet.from_file("report.xsd")
except xsdkit.SchemaError as e:
    for d in e.diagnostics:
        print(d)

Validation is different again: an invalid document is an answer, not an error, so validate returns a report and never raises for one.

report = schemas.validate(open("report.xml").read())
report.is_valid
report.errors        # errors only
report.diagnostics   # warnings and notes as well

What raises what

Exception Raised by
SchemaError SchemaSet.from_file, from_files, from_string and from_bytes, for a schema with errors
DocumentError decode, for a document that does not satisfy its schema
InvalidValueError Type.validate, for a value its type does not admit; also a ValueError
TypeError An argument of the wrong type, such as schemas.validate(42)
OSError A document path that cannot be read, such as a missing file
RuntimeError PsviEvents.report, read before every event has been

The first three are XsdErrors, and SchemaError and DocumentError carry every diagnostic on .diagnostics. Bytes that cannot be decoded are an invalid document like any other: a diagnostic in the report, and a DocumentError from decode.

Strict and lax

Conformance::Strict — the default — refuses to hand back a schema that had any error. Conformance::Lax downgrades the violations that still leave usable components behind, and a dangling xs:import is the one you will meet.

Use lax when you are reading someone else's schema to find out what is in it, and strict when you are checking your own before shipping it.

The codes

Grouped by the phase that raises them, which is also the order they can occur in.

10xx — reading the document

Code Meaning
XSD1001 Malformed XML
XSD1002 Root element is not xs:schema
XSD1003 Unknown element in the XSD namespace
XSD1004 A required attribute is missing
XSD1005 An attribute's value is not legal there
XSD1006 Unsupported character encoding
XSD1007 Bytes contradict the declared encoding
XSD1008 xs:annotation in a position the content model forbids
XSD1009 A required child element is absent

11xx — composition

Code Meaning
XSD1101 schemaLocation could not be resolved
XSD1102 xs:include of a document with a different target namespace
XSD1103 xs:import whose namespace does not match the document's
XSD1104 A construct this version does not support

12xx — resolution

Code Meaning
XSD1201 A reference names something that does not exist
XSD1202 Two global components with the same name in one symbol space
XSD1203 A circular definition

13xx — component validity

Code Meaning
XSD1301 A simple type is list and union and restriction at once
XSD1302 An illegal occurrence: minOccurs above maxOccurs, or an xs:all group where, or as often as, it may not appear
XSD1303 A type is defined two incompatible ways
XSD1304 The content model violates Unique Particle Attribution
XSD1305 A facet that does not apply to this type
XSD1306 A facet whose value is not legal
XSD1307 Facets that contradict each other
XSD1308 A default or fixed that its own type rejects
XSD1309 Derivation blocked by final or block
XSD1310 A restriction that does not restrict its base

20xx — instance validation

Code Meaning
XSD2001 No declaration for this element
XSD2002 An element the content model does not allow here
XSD2003 The content ended before the model was satisfied
XSD2004 A value its type rejects
XSD2005 An attribute not allowed here
XSD2006 A required attribute is missing
XSD2007 Text in element-only content
XSD2008 xsi:type names something unusable here
XSD2009 xsi:nil="true" on an element that has content
XSD2010 The type in force is abstract, so nothing validates against it
XSD2011 The same xs:ID value appears twice
XSD2012 An xs:IDREF names no xs:ID in the document
XSD2013 An abstract element appears in a document
XSD2014 Two nodes carry the same xs:key or xs:unique value
XSD2015 An xs:key field is absent
XSD2016 An xs:keyref matches no key
XSD2017 An xs:ENTITY names no unparsed entity in the DTD
XSD2018 A wildcard admitted a name the model declares with another type
XSD2019 xsi:nil on an element whose declaration is not nillable, or has a fixed value

Rendering

str(diagnostic) gives the compiler-style block shown above. In a notebook the same object renders as HTML, colour-coded by severity, and a ValidationReport renders as a summary line and a table — see In a notebook.