Appearance
When an ODD customisation misbehaves, the cause is almost always one of three things: a missing or misordered moduleRef, an element kept while the class or member it depends on was deleted, or constraints that never run because the Schematron was not extracted. Diagnose by regenerating the schema from a clean ODD, reading the processor's warnings literally, and validating a known-good TEI file against the result.
An ODD ("One Document Does it all") is itself a TEI document that declares your schema. Most "TEI bugs" are really ODD logic errors, and they have predictable fixes.
Why does my generated schema reject perfectly good TEI?
This is the most reported ODD problem. Two root causes dominate:
- You deleted a class, not just an element. TEI elements belong to attribute and model classes. If your ODD does
<classSpec ident="att.global" mode="delete"/>or removes a model class, every member loses its slot. Symptom: elements vanish from content models even though you never touched them. - A kept element depends on an excluded module. If you
moduleRefonlycoreandheaderbut keep an element fromtextcrit, the element has no home.
Fix: regenerate and inspect the module list first.
xml
<schemaSpec ident="myEdition" start="TEI">
<moduleRef key="tei"/>
<moduleRef key="core"/>
<moduleRef key="header"/>
<moduleRef key="textstructure"/>
<moduleRef key="textcrit"/> <!-- needed if you keep app/lem/rdg -->
</schemaSpec>Always include tei (the infrastructure module) and set start to your real root.
How do I actually process an ODD into a schema?
You never validate against the ODD directly — you compile it. Three routes:
| Method | Command / tool | Output |
|---|---|---|
| TEI Stylesheets | teitorelaxng myEdition.odd | .rng + Schematron |
| Roma | web app at the TEI site | .rng/.xsd/.dtd + docs |
| oXygen | right-click ODD → Generate Schema | schema + HTML reference |
If the processor emits warnings, stop and read them. A line like "element X referenced but its module is not included" is the exact diagnosis you need — do not ignore it and push on.
Why are my Schematron constraints not firing?
A very common, silent failure. Relax NG checks structure; it cannot express rules like "a rdg must point to a declared witness". You write those in a constraintSpec using Schematron — but the generated .rng does not enforce them. You must extract and run the companion .sch file:
xml
<constraintSpec ident="witCheck" scheme="schematron">
<constraint>
<sch:rule context="tei:rdg">
<sch:assert test="@wit">Every reading needs a @wit pointer.</sch:assert>
</sch:rule>
</constraint>
</constraintSpec>In oXygen, associate the generated .sch alongside the .rng. From the command line, run the file through an ISO Schematron processor. If you skip this step the rules look present but never trigger.
Why did my attribute change not take effect?
If you tried to constrain an attribute's values and nothing changed, check your mode. Editing an attDef requires mode="change" (or mode="replace"), and you must repeat the parent path. A frequent mistake is omitting mode, which defaults to add and creates a duplicate rather than changing the existing one:
xml
<elementSpec ident="div" mode="change">
<attList>
<attDef ident="type" mode="change">
<valList type="closed">
<valItem ident="chapter"/>
<valItem ident="letter"/>
</valList>
</attDef>
</attList>
</elementSpec>A type="closed" valList now rejects any @type value outside your list — the whole point of customising.
How should I add a new element without breaking interoperability?
Reuse before you invent. If no TEI element fits, add one with elementSpec mode="add", put it in your own namespace, and attach it to classes so it inherits global attributes and fits content models. Failing to add class membership is why custom elements often "can't be used anywhere" — they exist but no parent allows them. Document every addition in the ODD prose so collaborators understand the deviation.
What is a fast troubleshooting loop?
- Keep the ODD under version control and change one thing at a time.
- After each change, regenerate the schema and validate a small known-good file.
- Read processor warnings before touching your XML.
- Keep a tiny test corpus that exercises every customised element.
- When stuck, temporarily revert to
tei_allto confirm the problem is your ODD, not your TEI.
Key Takeaways
- Most "TEI errors" are ODD logic errors with predictable causes.
- Deleting a class removes all its members — check class dependencies first.
- Keep an element only if its module is referenced via
moduleRef. - Compile the ODD with the TEI Stylesheets, Roma, or oXygen; never validate the ODD itself.
- Schematron rules require running the generated
.schseparately — Relax NG won't enforce them. - Use the right
mode(change, not the defaultadd) when editing existing definitions.
Frequently Asked Questions
What is an ODD file in TEI?
ODD (One Document Does it all) is a TEI document that specifies your customisation: which modules, elements, and attributes you include, plus documentation and constraints. Processing it with Roma or the TEI Stylesheets generates your schema and reference docs.
Why does my ODD generate a schema that rejects valid TEI?
Usually because you set the wrong start element or excluded a module that a kept element depends on. Check moduleRef and that classes underlying your elements are present, since removing a class silently removes its members.
How do I turn my ODD into a usable schema?
Run it through the TEI Stylesheets (the teitorelaxng or teitoxsd transforms), the Roma web tool, or oXygen's built-in ODD processing. The output is a .rng, .xsd, or .dtd plus Schematron rules and HTML documentation.
Why are my Schematron rules being ignored?
The most common cause is forgetting to extract and run the Schematron separately, or putting constraints in the wrong namespace. Relax NG validation alone does not enforce constraintSpec rules; you must run the generated .sch file too.
Can I add my own element to TEI through an ODD?
Yes, but prefer reusing existing elements first. If you must add one, define it with elementSpec mode="add", give it a namespace, and attach it to appropriate classes so it inherits attributes and slots into content models correctly.