Help
The problems people actually hit, and the flag that tells each of them apart.
The big one
My schema runs, and finds nothing
schematron -s rules.sch --lintCause 1: a missing namespace prefix
XPath 1.0 has no default namespace, so an unprefixed name matches elements in no namespace. Against a namespaced document every context fails to match and nothing fires.
schematron -s rules.sch --verbose --phase '#ALL' data.xmlAn empty list of fired rules is the signature. The fix is to declare a
prefix with <ns> and use it everywhere:
<ns prefix="inv" uri="http://example.com/invoice"/>
<rule context="inv:invoice">
<assert test="inv:total">An invoice must have a total.</assert>
</rule>Cause 2: an earlier rule claimed the nodes
Within a single pattern, each node is processed by at most one rule: the first whose context matches it.
schematron -s rules.sch --explain--explain marks every rule after the first in a pattern with a
reminder that it only sees nodes no earlier rule claimed. The fix is to move
the independent checks into separate patterns — patterns do
not compete, and each gets its own pass over the document.
In your own tests, assert on it.
report.count_fired_rules() returning zero is the programmatic
form of this symptom. Check it and a broken schema fails loudly instead of
passing silently.
Frequently asked
Other things that surprise people
My assert fires when the document looks fine.
assert fires when its test is false; report fires when its test is true. If a
check fires on the good document and stays quiet on the bad one, the two
are swapped.
not(a = b) and a != b behave differently.
Correct, and both are sometimes what you want. Comparing a node-set with = in XPath 1.0 is existential: true if any node
compares true. So not(line/@type = 'discount') means "no line
is a discount", while line/@type != 'discount' means "some
line is not a discount".
A cross-reference check is slow, or does not work.
A cross-reference needs a key. The crate implements key and key() as a documented non-ISO extension — spec/keys/ explains why a
cross-reference check needs one, and what the alternative costs.
My schema uses an XPath 2.0 or 3.0 construct and the crate refuses it.
By design: outside the implemented subset you get a hard error naming the construct, rather than a plausible wrong answer. spec/xpath2/ and spec/xpath3/ list what is in and what is out for each.
An include over http: fails.
The default resolver refuses http: and https:,
and the command line tool has no network flag. Vendor the included schema
next to the one that includes it, or use the library with your own Resolver.
Turning on --parallel made things slower.
Likely, on a small document: the threads cost more than they save. Parallel patterns are a performance switch, not a behaviour switch — the report is identical either way, so measure and keep whichever is faster for your inputs.
A large document runs out of memory, or takes too much of it.
Try --stream (or Schema::validate_streaming):
it validates one repeating record at a time — the document element's
direct children, e.g. <order> under <orders> — instead of materialising the whole
document. Only for a schema whose rules are local to one record's own
subtree; anything else (key(), id(), document(), following::, a schema- or
phase-scoped <let>, among others) is refused by name
rather than silently falling back — a fallback that might exhaust the
memory you were trying to avoid using in the first place. See spec/streaming/ for exactly
what qualifies.
My schema works here but behaves differently under another processor.
Run schematron -s rules.sch --portability. It reports the
constructs that are correct here and treated differently by the ISO
reference implementation — a let that shadows an outer one, a
rule on comment(), a rule's @flag.
Still stuck?
Where to ask
Open an issue on GitHub. A schema, an input document, and what you expected is almost always enough — and if the answer turns out to be a gap in the specification, that is a defect worth fixing too.