An XML tool suddenly unable to find a DTD it found yesterday usually means the catalogue was hand-edited, and update-xmlcatalog is the fix.
It updates catalogue files used by XML tools, not the DTD or schema file itself.
Allow about 15 minutes for a single entry, including a backup and verification. This guide describes xml-core version 0.19 on the local system. The installed man page is dated 12 December 2023, so treat the exact command behaviour below as version-specific.
Confirm the command is installed before changing anything:
$ dpkg-query -W -f='${Package} ${Version}\n' xml-core
xml-core 0.19
$ command -v update-xmlcatalog
/usr/sbin/update-xmlcatalog
/etc/xml/catalog, the system-wide file.The entry type must be one of public, system or uri. The identifier is the exact public identifier, system URL or URI an XML consumer will request: keep the spelling and capitalisation exact.
Checkpoint: write down the identifier, type, package name and catalogue path before continuing. Most mistakes here are mismatched identifiers, not XML syntax errors.
Adding a root entry changes a system file and normally requires root privileges. Make a timestamped copy first; this command only reads the existing file and writes the backup:
$ sudo cp --preserve=all /etc/xml/catalog /etc/xml/catalog.before-update-xmlcatalog
If the file does not exist, stop and inspect the XML package installation instead of inventing a replacement. Do not copy a catalogue from another machine: its entries may describe packages and paths that do not exist here.
For a package called example-xml, with a local catalogue at /usr/share/xml/schema/example/catalog.xml, add a public identifier like this:
$ sudo update-xmlcatalog --verbose --add \
--package example-xml \
--local /usr/share/xml/schema/example/catalog.xml \
--type public \
--id '-//EXAMPLE//DTD EXAMPLE 1.0//EN'
--add creates the XML catalogue if it does not exist. The example uses a package-owned catalogue, so the package name is part of the bookkeeping. Replace every placeholder with values from the package's installation instructions or maintainer script.
For a system identifier, change only the type and identifier:
$ sudo update-xmlcatalog --verbose --add \
--package example-xml \
--local /usr/share/xml/schema/example/catalog.xml \
--type system \
--id 'https://example.invalid/dtd/example.dtd'
Tip: use --verbose while testing. Its output is diagnostic; the durable result is the catalogue content and the command's exit status.
Now connect the package entry to Debian's root catalogue:
$ sudo update-xmlcatalog --verbose --add \
--root \
--package example-xml \
--type public \
--id '-//EXAMPLE//DTD EXAMPLE 1.0//EN'
Check the resulting entry without editing the file by hand:
$ sudo grep -F -- '-//EXAMPLE//DTD EXAMPLE 1.0//EN' /etc/xml/catalog
<public publicId="-//EXAMPLE//DTD EXAMPLE 1.0//EN" uri="..."/>
The exact indentation and generated URI can vary. The useful check is that the identifier is present and points at the intended catalogue. To inspect the whole file, use less /etc/xml/catalog.
update-xmlcatalog keeps an internal database of the entries it manages and regenerates an indicated catalogue from that data. xmlcatalog, from libxml2-utils, edits a catalogue file directly. Do not use both tools for the same managed catalogue.
Warning: a direct xmlcatalog edit can appear to work and then vanish when update-xmlcatalog next updates that file. If an existing entry is missing, find the package or maintainer script that owns it and register it through update-xmlcatalog instead. Back up any manual work before experimenting.
The optional --sort flag sorts manipulated catalogue content. To sort without changing an entry, add an entry that is already present, with the same type, identifier and ownership details. Do not assume sorting alone repairs an incomplete database.
Deletion is also a state-changing operation. For a root entry, use the documented root form:
$ sudo update-xmlcatalog --verbose --del \
--root \
--type public \
--id '-//EXAMPLE//DTD EXAMPLE 1.0//EN'
A resulting empty XML catalogue is not automatically removed from disk. Check both the command status and the file:
$ sudo grep -F -- '-//EXAMPLE//DTD EXAMPLE 1.0//EN' /etc/xml/catalog || echo 'entry absent'
Recovery: if you need to undo an incorrect root change, restore the backup only when you are sure no legitimate catalogue updates happened after it was made:
$ sudo cp --preserve=all /etc/xml/catalog.before-update-xmlcatalog /etc/xml/catalog
That restore is deliberately conservative and can discard later changes. In a package-managed system, the safer long-term repair is to remove the bad registration with --del, then re-run the package's correct registration commands.
xml-core is installed and its version is recorded.update-xmlcatalog, not a conflicting direct editor./etc/xml/catalog contains the expected identifier and points at the intended catalogue.