Bug 35104 - We should warn when attempting to save MARC records that contain characters invalid in XML
Summary: We should warn when attempting to save MARC records that contain characters i...
Status: Pushed to main
Alias: None
Product: Koha
Classification: Unclassified
Component: Cataloging (show other bugs)
Version: Main
Hardware: All All
: P5 - low major
Target Milestone: ---
Assignee: Martin Renvoize (ashimema)
QA Contact: Nick Clemens (kidclamp)
URL:
Keywords:
: 41927 42413 42528 (view as bug list)
Depends on: 34549
Blocks: 34551 42873 42880 43020 43021 43022 43023 43024 43025 43026 43027 43028 43029 43643 41927
  Show dependency tree / graph
 
Reported: 2023-10-19 09:36 UTC by Martin Renvoize (ashimema)
Modified: 2026-09-25 17:59 UTC (History)
12 users (show)

See Also:
GIT URL:
Initiative type: ---
Strategic theme: ---
Sponsorship status: Sponsored
Comma delimited list of Sponsors: OpenFifth
Crowdfunding goal: 0
Crowdfunding committed: 0
Crowdfunding contact:
Patch complexity: Medium patch
Documentation contact:
Documentation submission:
Text to go in the release notes:
Version(s) released in:
26.11.00
Circulation function:


Attachments
Text file containing control characters (38 bytes, text/plain)
2023-10-23 02:01 UTC, David Cook
Details
Bug 34549: Alert when inserting text invalid in XML into bib record (3.74 KB, patch)
2023-10-23 04:36 UTC, David Cook
Details | Diff | Splinter Review
Bug 35104: Alert when inserting text invalid in XML into bib record (3.74 KB, patch)
2023-10-23 04:56 UTC, David Cook
Details | Diff | Splinter Review
Bug 35104: Alert when inserting text invalid in XML into bib record (3.81 KB, patch)
2023-10-23 11:29 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: [Alternative] Throw exception on store of invalid marcxml (3.08 KB, patch)
2023-12-06 11:53 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: [Alternative] Throw exception on store of invalid marcxml (3.18 KB, patch)
2023-12-06 15:01 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Initial attempt at catching the failure on new records (4.12 KB, patch)
2023-12-06 15:02 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Fix typo when catching exception (903 bytes, patch)
2023-12-07 06:27 UTC, David Cook
Details | Diff | Splinter Review
Bug 35104: [Alternative] Throw exception on store of invalid marcxml (3.18 KB, patch)
2023-12-13 16:19 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.12 KB, patch)
2023-12-13 16:19 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (3.17 KB, patch)
2023-12-13 16:49 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.12 KB, patch)
2023-12-13 16:49 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (7.98 KB, patch)
2024-05-08 16:42 UTC, Baptiste Bayche
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (3.22 KB, patch)
2024-10-22 13:35 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.19 KB, patch)
2024-10-22 13:35 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (3.28 KB, patch)
2025-01-20 14:25 UTC, Baptiste Wojtkowski (bwoj)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.07 KB, patch)
2025-01-20 14:25 UTC, Baptiste Wojtkowski (bwoj)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (3.29 KB, patch)
2025-01-21 09:49 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.26 KB, patch)
2025-01-21 09:49 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.35 KB, patch)
2025-01-21 09:49 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (6.11 KB, patch)
2025-01-21 13:10 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.84 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.52 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.23 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.96 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user (20.11 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Persist nonxml_stripped on biblio_metadata, show on detail pages (11.13 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Warn and log when non-XML characters are stripped in bulkmarcimport (2.14 KB, patch)
2026-02-26 13:31 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Biblio.t to reflect graceful non-XML stripping behaviour (3.30 KB, patch)
2026-02-26 13:43 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user (20.13 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Persist nonxml_stripped on biblio_metadata, show on detail pages (11.15 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Warn and log when non-XML characters are stripped in bulkmarcimport (2.17 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Biblio.t to reflect graceful non-XML stripping behaviour (3.32 KB, patch)
2026-02-26 13:45 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: DBIC Scheme Update (1.49 KB, patch)
2026-02-26 13:53 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user (20.13 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Persist nonxml_stripped on biblio_metadata, show on detail pages (11.83 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Warn and log when non-XML characters are stripped in bulkmarcimport (2.17 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Biblio.t to reflect graceful non-XML stripping behaviour (4.83 KB, patch)
2026-02-26 15:27 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: DBIC Scheme Update (1.15 KB, patch)
2026-02-26 15:28 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Exporter/Record.t to bypass store() validation (1.41 KB, patch)
2026-02-26 15:28 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-02-27 19:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-02-27 19:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-02-27 19:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-02-27 19:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user (20.13 KB, patch)
2026-02-27 19:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Persist nonxml_stripped on biblio_metadata, show on detail pages (11.83 KB, patch)
2026-02-27 19:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Warn and log when non-XML characters are stripped in bulkmarcimport (2.17 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Biblio.t to reflect graceful non-XML stripping behaviour (4.83 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: DBIC Scheme Update (1.15 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Exporter/Record.t to bypass store() validation (1.41 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (4.29 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.03 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Replace nonxml_stripped column with biblio_metadata_errors table (16.32 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Improve nonxml_stripped error context to cover all affected fields (6.73 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Record each stripped character as its own error with field and position context (12.50 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add context snippet with caret pointer to nonxml_stripped error messages (4.43 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Use Unicode Control Pictures in nonxml_stripped context snippets (3.05 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Import StripNonXmlChars in C4::Biblio (743 bytes, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Remove silent pre-emptive StripNonXmlChars call from TransformHtmlToMarc (1.64 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields (6.29 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form (8.26 KB, patch)
2026-02-27 19:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table (4.31 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (3.69 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.19 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (3.09 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully with per-field error tracking (11.73 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user on save (21.70 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Display non-XML stripping warnings on biblio detail pages (4.48 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields (4.92 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form (12.59 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update and extend tests to reflect graceful non-XML stripping (8.04 KB, patch)
2026-03-01 16:23 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Rogue record to trigger errors (1.75 KB, text/plain)
2026-03-01 16:27 UTC, Martin Renvoize (ashimema)
Details
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table (4.31 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (3.69 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.19 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (3.09 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully with per-field error tracking (11.73 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user on save (21.70 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Display non-XML stripping warnings on biblio detail pages (4.42 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields (4.92 KB, patch)
2026-03-16 10:17 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form (12.59 KB, patch)
2026-03-16 10:18 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update and extend tests to reflect graceful non-XML stripping (8.04 KB, patch)
2026-03-16 10:18 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Tests should be executable (573 bytes, patch)
2026-03-16 10:18 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-06-04 16:15 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-06-04 16:15 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-06-04 16:15 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-06-04 16:15 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table (4.31 KB, patch)
2026-06-04 16:15 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (3.69 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.19 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (3.09 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully with per-field error tracking (11.73 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user on save (21.69 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Display non-XML stripping warnings on biblio detail pages (4.47 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields (4.92 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form (12.59 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update and extend tests to reflect graceful non-XML stripping (7.53 KB, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Tests should be executable (573 bytes, patch)
2026-06-04 16:16 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table (4.31 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (3.69 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.19 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (3.09 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully with per-field error tracking (11.73 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user on save (21.69 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Display non-XML stripping warnings on biblio detail pages (4.47 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields (4.92 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form (12.59 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update and extend tests to reflect graceful non-XML stripping (7.53 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Tests should be executable (573 bytes, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) QA fixes (6.37 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Fix QA issues (3.17 KB, patch)
2026-06-04 17:22 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Throw exception on store of invalid marcxml (2.86 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on new and edited records (8.54 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch the failure on merging records (6.25 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add unit tests for exception on store (5.98 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table (4.31 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (3.69 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.19 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (3.09 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully with per-field error tracking (11.73 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Strip non-XML characters gracefully and notify user on save (21.69 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Display non-XML stripping warnings on biblio detail pages (4.47 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields (4.92 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form (12.59 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update and extend tests to reflect graceful non-XML stripping (7.53 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Tests should be executable (573 bytes, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) QA fixes (6.37 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Fix QA issues (3.17 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Simplify encode_error control flow in addbiblio.pl (4.11 KB, patch)
2026-06-04 17:30 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Surface invalid metadata errors during batch import (25.51 KB, patch)
2026-07-07 18:06 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Isolate metadata decode failures in other batch loops (10.05 KB, patch)
2026-07-07 18:06 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: (follow-up) Recover records with an empty datafield, generalize warning UI (31.34 KB, patch)
2026-07-07 18:06 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table for per-fault error tracking (4.14 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (4.05 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (938 bytes, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Guard Koha::Exceptions::Metadata::Invalid::full_message against undef fields (1.33 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (4.33 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Recover MARCXML that fails to decode, or throw when unrecoverable (29.42 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch invalid metadata and surface repair warnings in AddBiblio/ModBiblio (6.36 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Exporter/Record.t fixture for the new store() validation (1.32 KB, patch)
2026-07-08 12:04 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table for per-fault error tracking (4.29 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (4.20 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.07 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Guard Koha::Exceptions::Metadata::Invalid::full_message against undef fields (1.48 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (4.48 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Recover MARCXML that fails to decode, or throw when unrecoverable (29.58 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Catch invalid metadata and surface repair warnings in AddBiblio/ModBiblio (6.51 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Update Exporter/Record.t fixture for the new store() validation (1.47 KB, patch)
2026-07-08 12:24 UTC, Martin Renvoize (ashimema)
Details | Diff | Splinter Review
Bug 35104: Add biblio_metadata_errors table for per-fault error tracking (4.34 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Automated Schema Update (4.25 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Add koha_object_class to BiblioMetadataError schema result (1.12 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Guard Koha::Exceptions::Metadata::Invalid::full_message against undef fields (1.53 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes (4.53 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Recover MARCXML that fails to decode, or throw when unrecoverable (29.63 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Catch invalid metadata and surface repair warnings in AddBiblio/ModBiblio (6.57 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: Update Exporter/Record.t fixture for the new store() validation (1.52 KB, patch)
2026-08-06 11:40 UTC, Nick Clemens (kidclamp)
Details | Diff | Splinter Review
Bug 35104: (RM follow-up) Fix regressions.t for Metadata's new auto-repair (1.47 KB, patch)
2026-08-19 11:02 UTC, Pedro Amorim (ammopt)
Details | Diff | Splinter Review
Bug 35104: (RM follow-up) Restore StripNonXmlChars in TransformHtmlToMarc (1.78 KB, patch)
2026-08-19 14:31 UTC, Pedro Amorim (ammopt)
Details | Diff | Splinter Review

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Renvoize (ashimema) 2023-10-19 09:36:51 UTC
Bug 34549 adds some defensive coding to prevent incorrectly encoded MARC data getting into the database.. but is does so by silently stripping bad characters from the record.

It would be nice to highlight that this is taking place, or preferably before it takes place so the end user understands what's happening.
Comment 1 David Cook 2023-10-23 00:00:41 UTC
When I was writing bug 34549, I thought a little bit about how this might be done. Whether it was better to do it by Javascript on the page, an AJAX call, or do it on page submission server side...
Comment 2 David Cook 2023-10-23 00:10:07 UTC
But first a test...

I change the Library of Congress Z39.50 from utf8 to "ISO_8859-1" (note changing to MARC-8 didn't seem to make a difference), and then I search "bibliothecaire" for the Title.

I quickly notice obvious encoding problems like "Fonction peÌdagogique du documentaliste-bibliotheÌcaire"

So I go to import that one... and actually I don't have any problems. I get a record with the following title: "Fonction peÌdagogique du documentaliste-bibliotheÌcaire : journeÌe acadeÌmique des documentalistes-bibliotheÌcaires, le 7 feÌvrier 1977"

I suppose it's because Ì and  are valid UTF-8 characters. 

If I change the Z39.50 back to "utf8" then I get: "Fonction pédagogique du documentaliste-bibliothécaire :"

I'm not sure why é would be interpreted as Ì though. If I download as MARCXML, I see the title is "Fonction pédagogique du documentaliste-bibliothécaire :"

I guess I'm not confident how many conversions are happening and at which points but this still looks odd...
Comment 3 David Cook 2023-10-23 01:04:01 UTC
As per my comment at https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=34549#c19 technically the problem we're trying to solve in bug 34549 is preventing the saving of characters that cannot be rendered in XML. 

Picking up on encoding problems is a lot trickier because bytes can be valid in multiple different encodings. The machine can still output a valid character, so it doesn't realize that the source data is encoded incorrectly.
Comment 4 David Cook 2023-10-23 02:01:42 UTC Comment hidden (obsolete)
Comment 5 David Cook 2023-10-23 02:27:33 UTC
I've written a Javascript based check which *mostly* gets us there. It checks the input[text] and textarea fields for bytes that aren't allowed in XML and it raises an alert if it finds any.

I haven't figured out a way to get the "Go to field" to work though, as I need the tab where the HTML element is found, but we aren't recording that data anywhere at the moment. We have a "tabindex", but it seems hard-coded to 0 or 1 for some reason...

I would attach my patch here on Bugzilla, but "git-bz" isn't working for me right now...

I've run out of steam on this one anyway, but hopefully I can share my code and someone else can take it away...
Comment 6 David Cook 2023-10-23 04:36:15 UTC Comment hidden (obsolete)
Comment 7 David Cook 2023-10-23 04:39:13 UTC
git-bz still not working for me, so just used "git format-patch -1 HEAD" and uploaded manually...
Comment 8 David Cook 2023-10-23 04:56:37 UTC Comment hidden (obsolete)
Comment 9 Martin Renvoize (ashimema) 2023-10-23 11:29:36 UTC Comment hidden (obsolete)
Comment 10 Martin Renvoize (ashimema) 2023-10-23 11:32:34 UTC
This works really nicely, as such I'm signing off...

Two little points however.. 

1) I wonder if we could/should merge these errors into the existing validation alert rather than having it's own alert box?
2) I did consider whether we should go belt & braces and catch on the server too.. however I think this is a great improvement without that anyway.
Comment 11 Martin Renvoize (ashimema) 2023-10-23 11:33:29 UTC
(In reply to David Cook from comment #3)
> As per my comment at
> https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=34549#c19
> technically the problem we're trying to solve in bug 34549 is preventing the
> saving of characters that cannot be rendered in XML. 
> 
> Picking up on encoding problems is a lot trickier because bytes can be valid
> in multiple different encodings. The machine can still output a valid
> character, so it doesn't realize that the source data is encoded incorrectly.

You're totally right there.. it's really hard to spot encoding issues becuase of this :(
Comment 12 David Cook 2023-10-24 00:10:10 UTC
While I appreciate the sign off, it's not quite ready yet. It doesn't calculate the tab currently for the "Go to field" link. It only works by accident for things like the 245 due to "tabindex" weirdness.
Comment 13 David Cook 2023-10-24 00:12:04 UTC
(In reply to Martin Renvoize from comment #10)
> This works really nicely, as such I'm signing off...
> 
> Two little points however.. 
> 
> 1) I wonder if we could/should merge these errors into the existing
> validation alert rather than having it's own alert box?

That was my original intention but that would involve more refactoring. Could be worth doing though.

> 2) I did consider whether we should go belt & braces and catch on the server
> too.. however I think this is a great improvement without that anyway.

I was pondering that too. Probably worth doing.
Comment 14 David Cook 2023-10-31 03:50:34 UTC
I had an interesting experience that I thought I'd share here since it's relevant.

One of my librarians was copying and pasting text from a PDF into Koha. When they did it, it generated a broken MARC record. When I did it, it worked fine.

That's when I discovered that Chrome, Firefox, and Edge all treat PDF text differently.

Consider the following phrase "like-minded people" which is actually broken against 2 lines on the hyphen in the PDF. If you copy from Chrome, it removes the hyphen so it becomes "likeminded people". If you copy from Firefox, it copies the hyphen and a line break so it becomes something like "like-\nminded people". If you copy from Edge, it mangles the hyphen and turns it into a "Start of text" control character, which will break Koha.

--

While we work through our official solution, I came up with a little Javascript function which is run during the "paste" event in the MARC editor. It displays a "confirm" box which contains an explanatory warning, offers a tip on understanding the problem, and then provides an option to try to "fix" the record (by stripping out the bad characters).

I'm just running that in their "IntranetUserJS" for now, but I thought I'd share this information. 

I think having a warning at "save time" is very useful especially for imported records. This "on paste" warning might also be useful though since it's at the time that they're entering the data, so it might be easier for them to notice the problem.

--

In any case, it was great to find an example from the wild to test a fix against...
Comment 15 Martin Renvoize (ashimema) 2023-10-31 06:28:20 UTC
Wow, that's an impressive find. Man this stuff ends up in fun "exciting" places.
Comment 16 David Cook 2023-10-31 23:26:56 UTC
(In reply to Martin Renvoize from comment #15)
> Wow, that's an impressive find. Man this stuff ends up in fun "exciting"
> places.

Thanks! I did feel pretty good working that one out!

During this process, I've been thinking there might be an alternative to bug 34549 as well.

Instead of erasing the invalid characters, surely we could just escape them.

So I'm going to use my discovery from https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=35104#c14 to see if I can do an alternate version of bug 34549 where we escape the bad characters using numeric character references or an entity.

So the STX character from that example would be converted to something like  instead of just erased.

Of course, it would be good to try it out with a variety of examples and not just this sort of data corruption example.

But maybe it's also one of those ones where we can just do our best and see how it evolves over time...
Comment 17 David Cook 2023-10-31 23:50:44 UTC
(In reply to David Cook from comment #16)
> So the STX character from that example would be converted to something like
>  instead of just erased.

I just tried a client-side replace where the STX character gets converted into  but it looks like it gets converted into  

Something I find interesting is that in the database we'll store "David’s awesome record" but then the MARCXML export to the browser will encode the UTF-8 encoded character ’ as ’

I'm going to poke around in this a bit more...
Comment 18 David Cook 2023-11-01 00:43:12 UTC
(In reply to David Cook from comment #17)
> I'm going to poke around in this a bit more...

The TransformHtmlToMarc doesn't seem to affect it...

If I do $record->as_formatted then I see:

likeminded

If I do $record->as_xml then I see:



Looking at https://metacpan.org/dist/MARC-File-XML/source/lib/MARC/File/XML.pm#L378 there is an escape function that escapes ampersands and angle brackets. 

In theory, maybe MARC::File::XML should escape any invalid characters using character references or remove them since they're invalid.

But MARC::File::XML's escaping means it's impossible for us to pre-escape any invalid characters.

It feels like MARC::File::XML is essentially holding us hostage. We need to clean our input data (in whatever format) before it reaches MARC::File::XML, which seems a bit silly, since it's the XML format which has the restrictions...

That being said... the XML 1.0 spec is pretty forgiving. After review, it's really just excluding *some* ASCII control characters, Unicode surrogates, U+FFFE, and U+FFFF. That's a really small number of characters and none of them are printable characters.
Comment 19 David Cook 2023-11-01 00:48:25 UTC
(In reply to David Cook from comment #18)
> That being said... the XML 1.0 spec is pretty forgiving. After review, it's
> really just excluding *some* ASCII control characters, Unicode surrogates,
> U+FFFE, and U+FFFF. That's a really small number of characters and none of
> them are printable characters.

After review, for a UTF-8 encoded document, bug 34549 would only strip non-printable characters. 

I might have another crack at trying to get a Latin-1 encoded document into Koha... as I know that I've had Latin-1 encoded data in Koha before (although it's very possible that it came through side-loading using non-Koha tools).
Comment 20 David Cook 2023-11-01 02:28:19 UTC
We've got an interesting assortment of fixes at the moment...

If you already have a record with invalid characters, you can open it using bug 34014 which will scrub your record clean within the editor, but it will also show you the parser errors, so you could go and repair your record, in theory.

With bug 34549, you can try to save invalid characters, and it will silently wipe them out. 

With bug 35104, I've been looking at a client-side warning, but it doesn't tell you exactly which characters are a problem. 

Maybe we'd be better off re-using the mechanism from bug 34014 here in bug 35104. That would remove the need for a client-side check, it would be more consistent, and then we could undo bug 34549...
Comment 21 Martin Renvoize (ashimema) 2023-12-06 11:53:23 UTC Comment hidden (obsolete)
Comment 22 Martin Renvoize (ashimema) 2023-12-06 11:54:57 UTC
I think we could perhaps go a level lower and put this exception into Koha::Biblio::Metadata->store maybe instead?

We'll also need to account for the exception at various places with a try/catch block as detailed in the commit message.. but I wanted to start getting feedback on the proposed approach before committing to continuing down this path.
Comment 23 Martin Renvoize (ashimema) 2023-12-06 15:01:57 UTC Comment hidden (obsolete)
Comment 24 Martin Renvoize (ashimema) 2023-12-06 15:02:00 UTC Comment hidden (obsolete)
Comment 25 David Cook 2023-12-06 22:53:48 UTC
Thanks for working on this, Martin. I'll try to take a look in a bit.
Comment 26 David Cook 2023-12-07 06:27:02 UTC
I've applied the last 2 patches and restarted Koha...

Bug 35104: [Alternative] Throw exception on store of invalid marcxml 
Bug 35104: Initial attempt at catching the failure on new records 

And then I added the text from "Text file containing control characters" into a test record

--

I'm getting an error trace page.

It looks like you made a little mistake when catching the exception and it's causing you to rethrow it. I'll fix that up in a sec... 

--

However, with the fix, we're still having a problem rendering the page. But I'll let folk take it from there...
Comment 27 David Cook 2023-12-07 06:27:45 UTC Comment hidden (obsolete)
Comment 28 David Cook 2023-12-07 06:28:43 UTC
I have to run and I'm away tomorrow morning, but hopefully this gets you a bit further.
Comment 29 Martin Renvoize (ashimema) 2023-12-07 13:50:23 UTC
Awesome.. I knew it was something silly when catching the exception!

That gives me enough to get going again :), thanks David
Comment 30 David Cook 2023-12-08 03:26:07 UTC
(In reply to Martin Renvoize from comment #29)
> Awesome.. I knew it was something silly when catching the exception!
> 
> That gives me enough to get going again :), thanks David

Yay teamwork! :D
Comment 31 Martin Renvoize (ashimema) 2023-12-13 16:19:50 UTC Comment hidden (obsolete)
Comment 32 Martin Renvoize (ashimema) 2023-12-13 16:19:53 UTC Comment hidden (obsolete)
Comment 33 Martin Renvoize (ashimema) 2023-12-13 16:49:17 UTC Comment hidden (obsolete)
Comment 34 Martin Renvoize (ashimema) 2023-12-13 16:49:20 UTC Comment hidden (obsolete)
Comment 35 Jonathan Druart 2023-12-14 10:43:17 UTC
IMO this is too low level, we should not call MARC::Record::new_from_xml everytime we store.

It will work ofc but what about perf?

What if I do want invalid marcxml? :D
Comment 36 David Cook 2023-12-14 23:39:56 UTC
(In reply to Jonathan Druart from comment #35)
> IMO this is too low level, we should not call MARC::Record::new_from_xml
> everytime we store.
> 
> It will work ofc but what about perf?

I think that we should validate each time we store. However... maybe we could use XML::LibXML directly instead of MARC::Record to reduce some overhead. I don't know if it makes an actual difference in terms of perf though. And the nice thing about using MARC::Record is we know if it will break on subsequent usage.

Perhaps more importantly, these patches don't add error handling for every instance of Koha::Biblio::Metadata->store(). We might be breaking Staged MARC imports here and not realizing it. 

So maybe we should add a Koha::Biblio::Metadata->validate() and just call it from the controller script for now? 

> What if I do want invalid marcxml? :D

:P
Comment 37 Baptiste Bayche 2024-05-08 16:42:52 UTC Comment hidden (obsolete)
Comment 38 Martin Renvoize (ashimema) 2024-06-12 11:28:28 UTC
Thanks for the follow-up here Baptiste.. it's great to see someone is still interested in this.

I'll continue to add such catches elsewhere as it seems people do actually want to help pursue it.
Comment 39 David Cook 2024-06-12 23:20:56 UTC
We should have at least one unit test for this.

I was thinking that maybe we should move the code into a validate() function and call that from store() but it doesn't matter too much, as we can unit test it in store() just fine too. 

Martin, if you want to add that unit test, I could look at QAing this.

Invalid metadata is a pet peeve of mine, so I'd love to see this (and related changes) get in.
Comment 40 Kyle M Hall (khall) 2024-10-18 11:14:31 UTC
Getting sha1 errors. Can you post a branch on github?
Comment 41 David Cook 2024-10-22 03:33:20 UTC
I too get the sha1 error:

Applying: Bug 35104: Throw exception on store of invalid marcxml
Applying: Bug 35104: Catch the failure on new and edited records
error: sha1 information is lacking or useless (C4/Biblio.pm).
error: could not build fake ancestor
Patch failed at 0001 Bug 35104: Catch the failure on new and edited records

--

Interestingly, the first patch applied but not the second one. If you dig into it, you can see that the first index referenced here doesn't exist in the upstream git:

diff --git a/C4/Biblio.pm b/C4/Biblio.pm
index 0d6feb8aed..eb79c397a5 100644

Since there is over 3 hours between commits, the second one must've been based off a local index state for C4/Biblio.pm. Funny enough, the other file changed does have an index from the upstream: a11711afd3

Just needs a rebase.
Comment 42 Martin Renvoize (ashimema) 2024-10-22 13:35:24 UTC Comment hidden (obsolete)
Comment 43 Martin Renvoize (ashimema) 2024-10-22 13:35:27 UTC Comment hidden (obsolete)
Comment 44 Martin Renvoize (ashimema) 2024-10-22 13:39:49 UTC
Rebased.. I started working on other cases but ran out of time for this.. there's a lot of calls to ModBiblio in Koha that would benefit from try/catch treatment after this and propagating the errors to screen or gracefully skipping over them.
Comment 45 Baptiste Wojtkowski (bwoj) 2025-01-20 14:25:36 UTC Comment hidden (obsolete)
Comment 46 Baptiste Wojtkowski (bwoj) 2025-01-20 14:25:39 UTC Comment hidden (obsolete)
Comment 47 Baptiste Wojtkowski (bwoj) 2025-01-20 14:32:05 UTC Comment hidden (obsolete)
Comment 48 Katrin Fischer 2025-01-20 14:35:27 UTC
(In reply to Baptiste Wojtkowski (bwoj) from comment #46)
> Created attachment 176821 [details] [review] [review]
> Bug 35104: Catch the failure on new and edited records
> 
> We now catch the error and display it to the end user so they can fix
> the issues before attempting to save again.
> 
> Signed-off-by: baptiste <baptiste.bayche@inlibro.com>
> Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>

Can we fix baptiste (1) sign-off line to save me a mailmap mapping please? :)
Comment 49 Martin Renvoize (ashimema) 2025-01-21 09:49:47 UTC Comment hidden (obsolete)
Comment 50 Martin Renvoize (ashimema) 2025-01-21 09:49:51 UTC Comment hidden (obsolete)
Comment 51 Martin Renvoize (ashimema) 2025-01-21 09:49:54 UTC Comment hidden (obsolete)
Comment 52 Martin Renvoize (ashimema) 2025-01-21 13:10:24 UTC Comment hidden (obsolete)
Comment 53 Martin Renvoize (ashimema) 2025-01-21 13:12:47 UTC
So.. we'll have other failing tests.. t/db_dependant/Biblio.t for example.. this is because we're actually testing that putting bad characters into the database should succeed and with this patchset it doesn't.. given that's the idea of this patchset.. to prevent bad data ever making it's way into the database I'm not sure how to proceed.

We can continue to add tests for bad data to ensure we capture it and deal with it when reading data out (should someone decided to put it in directly using database queries).. but should we continue to allow ModBiblio to "press on regardless".. personally I think not.
Comment 54 Baptiste Wojtkowski (bwoj) 2025-01-21 16:49:28 UTC
Is SO the correct state ?
Comment 55 Martin Renvoize (ashimema) 2026-02-26 13:31:49 UTC Comment hidden (obsolete)
Comment 56 Martin Renvoize (ashimema) 2026-02-26 13:31:51 UTC Comment hidden (obsolete)
Comment 57 Martin Renvoize (ashimema) 2026-02-26 13:31:52 UTC Comment hidden (obsolete)
Comment 58 Martin Renvoize (ashimema) 2026-02-26 13:31:53 UTC Comment hidden (obsolete)
Comment 59 Martin Renvoize (ashimema) 2026-02-26 13:31:55 UTC Comment hidden (obsolete)
Comment 60 Martin Renvoize (ashimema) 2026-02-26 13:31:57 UTC Comment hidden (obsolete)
Comment 61 Martin Renvoize (ashimema) 2026-02-26 13:31:58 UTC Comment hidden (obsolete)
Comment 62 Martin Renvoize (ashimema) 2026-02-26 13:43:54 UTC Comment hidden (obsolete)
Comment 63 Martin Renvoize (ashimema) 2026-02-26 13:45:38 UTC Comment hidden (obsolete)
Comment 64 Martin Renvoize (ashimema) 2026-02-26 13:45:39 UTC Comment hidden (obsolete)
Comment 65 Martin Renvoize (ashimema) 2026-02-26 13:45:41 UTC Comment hidden (obsolete)
Comment 66 Martin Renvoize (ashimema) 2026-02-26 13:45:43 UTC Comment hidden (obsolete)
Comment 67 Martin Renvoize (ashimema) 2026-02-26 13:45:44 UTC Comment hidden (obsolete)
Comment 68 Martin Renvoize (ashimema) 2026-02-26 13:45:46 UTC Comment hidden (obsolete)
Comment 69 Martin Renvoize (ashimema) 2026-02-26 13:45:47 UTC Comment hidden (obsolete)
Comment 70 Martin Renvoize (ashimema) 2026-02-26 13:45:49 UTC Comment hidden (obsolete)
Comment 71 Martin Renvoize (ashimema) 2026-02-26 13:53:11 UTC Comment hidden (obsolete)
Comment 72 Martin Renvoize (ashimema) 2026-02-26 15:27:49 UTC Comment hidden (obsolete)
Comment 73 Martin Renvoize (ashimema) 2026-02-26 15:27:51 UTC Comment hidden (obsolete)
Comment 74 Martin Renvoize (ashimema) 2026-02-26 15:27:52 UTC Comment hidden (obsolete)
Comment 75 Martin Renvoize (ashimema) 2026-02-26 15:27:54 UTC Comment hidden (obsolete)
Comment 76 Martin Renvoize (ashimema) 2026-02-26 15:27:55 UTC Comment hidden (obsolete)
Comment 77 Martin Renvoize (ashimema) 2026-02-26 15:27:56 UTC Comment hidden (obsolete)
Comment 78 Martin Renvoize (ashimema) 2026-02-26 15:27:58 UTC Comment hidden (obsolete)
Comment 79 Martin Renvoize (ashimema) 2026-02-26 15:27:59 UTC Comment hidden (obsolete)
Comment 80 Martin Renvoize (ashimema) 2026-02-26 15:28:01 UTC Comment hidden (obsolete)
Comment 81 Martin Renvoize (ashimema) 2026-02-26 15:28:02 UTC Comment hidden (obsolete)
Comment 82 Martin Renvoize (ashimema) 2026-02-26 15:29:47 UTC
A good way to test this is to attempt to import the file from bug 41927 in every way you can think of and confirm a) it imports and b) it shows the error in the UI for the biblio view.
Comment 83 Martin Renvoize (ashimema) 2026-02-26 23:50:00 UTC
*** Bug 41927 has been marked as a duplicate of this bug. ***
Comment 84 David Cook 2026-02-27 03:20:28 UTC
I'm keen to move this one along but can we get a consolidated test plan?
Comment 85 David Cook 2026-02-27 03:24:42 UTC
In cataloguing/addbiblio.pl why is "elsif ($encode_error)" at the bottom? Wouldn't it make more sense to put that near the top of the if/elsif and that way you could avoid adding all the "!$encode_error" condition to the other elsif statements?
Comment 86 David Cook 2026-02-27 03:27:12 UTC
Hmm I see the merit of having "nonxml_stripped" as a DB column but it's a bit weird to have it in the "biblio_metadata" table since the metadata in that table doesn't have to be XML theoretically. 

But then I don't know about making another DB table like "biblio_metadata_errors"...
Comment 87 Martin Renvoize (ashimema) 2026-02-27 19:23:51 UTC Comment hidden (obsolete)
Comment 88 Martin Renvoize (ashimema) 2026-02-27 19:23:52 UTC Comment hidden (obsolete)
Comment 89 Martin Renvoize (ashimema) 2026-02-27 19:23:54 UTC Comment hidden (obsolete)
Comment 90 Martin Renvoize (ashimema) 2026-02-27 19:23:55 UTC Comment hidden (obsolete)
Comment 91 Martin Renvoize (ashimema) 2026-02-27 19:23:57 UTC Comment hidden (obsolete)
Comment 92 Martin Renvoize (ashimema) 2026-02-27 19:23:58 UTC Comment hidden (obsolete)
Comment 93 Martin Renvoize (ashimema) 2026-02-27 19:24:00 UTC Comment hidden (obsolete)
Comment 94 Martin Renvoize (ashimema) 2026-02-27 19:24:01 UTC Comment hidden (obsolete)
Comment 95 Martin Renvoize (ashimema) 2026-02-27 19:24:03 UTC Comment hidden (obsolete)
Comment 96 Martin Renvoize (ashimema) 2026-02-27 19:24:04 UTC Comment hidden (obsolete)
Comment 97 Martin Renvoize (ashimema) 2026-02-27 19:24:06 UTC Comment hidden (obsolete)
Comment 98 Martin Renvoize (ashimema) 2026-02-27 19:24:07 UTC Comment hidden (obsolete)
Comment 99 Martin Renvoize (ashimema) 2026-02-27 19:24:09 UTC Comment hidden (obsolete)
Comment 100 Martin Renvoize (ashimema) 2026-02-27 19:24:10 UTC Comment hidden (obsolete)
Comment 101 Martin Renvoize (ashimema) 2026-02-27 19:24:12 UTC Comment hidden (obsolete)
Comment 102 Martin Renvoize (ashimema) 2026-02-27 19:24:13 UTC Comment hidden (obsolete)
Comment 103 Martin Renvoize (ashimema) 2026-02-27 19:24:15 UTC Comment hidden (obsolete)
Comment 104 Martin Renvoize (ashimema) 2026-02-27 19:24:17 UTC Comment hidden (obsolete)
Comment 105 Martin Renvoize (ashimema) 2026-02-27 19:24:18 UTC Comment hidden (obsolete)
Comment 106 Martin Renvoize (ashimema) 2026-02-27 19:24:20 UTC Comment hidden (obsolete)
Comment 107 Martin Renvoize (ashimema) 2026-02-27 19:24:21 UTC Comment hidden (obsolete)
Comment 108 Martin Renvoize (ashimema) 2026-03-01 16:23:35 UTC Comment hidden (obsolete)
Comment 109 Martin Renvoize (ashimema) 2026-03-01 16:23:37 UTC Comment hidden (obsolete)
Comment 110 Martin Renvoize (ashimema) 2026-03-01 16:23:38 UTC Comment hidden (obsolete)
Comment 111 Martin Renvoize (ashimema) 2026-03-01 16:23:39 UTC Comment hidden (obsolete)
Comment 112 Martin Renvoize (ashimema) 2026-03-01 16:23:41 UTC Comment hidden (obsolete)
Comment 113 Martin Renvoize (ashimema) 2026-03-01 16:23:43 UTC Comment hidden (obsolete)
Comment 114 Martin Renvoize (ashimema) 2026-03-01 16:23:44 UTC Comment hidden (obsolete)
Comment 115 Martin Renvoize (ashimema) 2026-03-01 16:23:46 UTC Comment hidden (obsolete)
Comment 116 Martin Renvoize (ashimema) 2026-03-01 16:23:47 UTC Comment hidden (obsolete)
Comment 117 Martin Renvoize (ashimema) 2026-03-01 16:23:49 UTC Comment hidden (obsolete)
Comment 118 Martin Renvoize (ashimema) 2026-03-01 16:23:50 UTC Comment hidden (obsolete)
Comment 119 Martin Renvoize (ashimema) 2026-03-01 16:23:51 UTC Comment hidden (obsolete)
Comment 120 Martin Renvoize (ashimema) 2026-03-01 16:23:53 UTC
Created attachment 194225 [details] [review]
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form

Extend the data quality warnings banner in the MARC editor with a
per-error 'Mark resolved' button.  Clicking it calls a new REST endpoint
(DELETE /api/v1/biblios/{biblio_id}/metadata/errors/{error_id}) which
removes the corresponding biblio_metadata_errors row.  The button is then
hidden client-side to give immediate feedback.

The new Koha::REST::V1::Biblios::MetadataErrors controller handles the
delete, verifying that the requested error belongs to the requested biblio
before removing it.

Sponsored-by: OpenFifth
Comment 121 Martin Renvoize (ashimema) 2026-03-01 16:23:54 UTC
Created attachment 194226 [details] [review]
Bug 35104: Update and extend tests to reflect graceful non-XML stripping

* t/db_dependent/Koha/Biblio/Metadata.t: replace tests that expected an
  exception on bad MARCXML with tests that verify graceful stripping
  behaviour, per-field error recording, context snippet format, the
  stripped_on_last_store flag, and preservation of existing error rows on
  a clean re-save.

* t/db_dependent/Biblio.t: remove tests that relied on the exception path
  and update assertions to match the graceful-stripping model.

* t/db_dependent/Exporter/Record.t: bypass store() validation for a test
  record that intentionally contains bad data so the export-path tests
  remain independent of the stripping logic.

Sponsored-by: OpenFifth
Comment 122 Martin Renvoize (ashimema) 2026-03-01 16:27:17 UTC
Created attachment 194227 [details]
Rogue record to trigger errors
Comment 123 Martin Renvoize (ashimema) 2026-03-01 16:31:21 UTC
Please find attached to the patchset a rogue record that will trigger the xml character stripping as refined here.

Test plan
1) Use the record attached, loading it into the system via 'stage + commit' or 'bulkmarkimport' or any other means you see fit
2) Note that the record loads, warnings may be displayed on the command line however.
3) Navigate to the new record, the page should load but show a message at the top with details that the record was modified on import and how it was modified.
4) Edit the record and mark the issue as resolved
5) The warning message should no longer appear
Comment 124 David Nind 2026-03-01 18:03:30 UTC
Some initial testing.

Maybe we should ask some cataloguers for same sample records that should cause issues?

First attempted test
--------------------

I attempted to test, but getting this error in several catalog areas in the staff interface after importing the sample record:
  The method Koha::Biblio::Metadata->metadata_errors is not covered by tests!
  Trace begun at /kohadevbox/koha/Koha/Object.pm line 1042

Areas include:
- Accessing a record from search results
- Trying to directly access a record
- View and edit actions for manage stated MARC records

This only happens after importing the sample record.

I did a reset_all, then updatedatabase, dbic, yarn build, and a restart_all, and didn't get this error.

Imported record
---------------

Viewing the imported record in the staff interface:

Record was automatically modified
Non-XML characters were stripped from this record when it was last saved. Please review and re-save the record to confirm the data is correct.

336$a: invalid char value 8 (U+0008) at position 12
    t $xtac arr␈\ $btxt V $2rdac ontent
               ^

Resolved error and updated other MARC issues, can view and edit fine.

Testing notes
-------------

Testing notes so far (using KTD):
1. Apply the patch
2. Run the database update: updatedatabase
3. Run DBIC: dbic (not sure if this is required)
4. Test importing the sample record using Cataloging > Stage record for import and Manage staged record
5. Run tests prove t/db_dependent/Koha/Biblio/Metadata.t  t/db_dependent/Biblio.t t/db_dependent/Exporter/Record.t
6. Bonus tests:
   - Merge a record
   - Import  record from the command line
   - Z39.50 import
Comment 125 Martin Renvoize (ashimema) 2026-03-02 14:24:34 UTC
Hi David, thanks for testing.

I'm struggling to replicate the issue your seeing..

I started with a ktd that started on main, then I checked out the branch and ran through updatedatabase followed by a restart_all.

I imported my record using stage then import.

Searching worked for me, direct navigation worked etc.

I then tried a reset_all and uploaded the rogue record again.. all continue to work for me.

Happy to discus on Mattermost if it's easier?
Comment 126 Lisette Scheer 2026-03-12 20:25:04 UTC

I didn't have the same problem as David. David, did you restart_all?

Everything worked fine for me except the qa tool:
[SKIP] Koha/Biblio/Metadata/Error.pm
   SKIP   pod_coverage
                No POD exists
[FAIL] t/db_dependent/Koha/Biblio/Metadata.t
   FAIL   forbidden_patterns
                forbidden pattern: Use https:// instead of http:// for URLs (line 58)
[FAIL] t/db_dependent/api/v1/biblios_metadata_errors.t
   FAIL   file_permissions
                File must have the exec flag
Comment 127 Martin Renvoize (ashimema) 2026-03-16 10:17:19 UTC
(In reply to Lisette Scheer from comment #126)
> 
> I didn't have the same problem as David. David, did you restart_all?
> 
> Everything worked fine for me except the qa tool:
> [SKIP] Koha/Biblio/Metadata/Error.pm
>    SKIP   pod_coverage
>                 No POD exists

False negative.. new files end up with the POD failure even when there's POD for some reason.

> [FAIL] t/db_dependent/Koha/Biblio/Metadata.t
>    FAIL   forbidden_patterns
>                 forbidden pattern: Use https:// instead of http:// for URLs

False negative.. it's http inside test data inside the standard xslt

> (line 58)
> [FAIL] t/db_dependent/api/v1/biblios_metadata_errors.t
>    FAIL   file_permissions
>                 File must have the exec flag

Could have sworn I'd fixed this already.. follow-up attached anyways.
Comment 128 Martin Renvoize (ashimema) 2026-03-16 10:17:41 UTC Comment hidden (obsolete)
Comment 129 Martin Renvoize (ashimema) 2026-03-16 10:17:42 UTC Comment hidden (obsolete)
Comment 130 Martin Renvoize (ashimema) 2026-03-16 10:17:44 UTC Comment hidden (obsolete)
Comment 131 Martin Renvoize (ashimema) 2026-03-16 10:17:46 UTC Comment hidden (obsolete)
Comment 132 Martin Renvoize (ashimema) 2026-03-16 10:17:47 UTC Comment hidden (obsolete)
Comment 133 Martin Renvoize (ashimema) 2026-03-16 10:17:49 UTC Comment hidden (obsolete)
Comment 134 Martin Renvoize (ashimema) 2026-03-16 10:17:50 UTC Comment hidden (obsolete)
Comment 135 Martin Renvoize (ashimema) 2026-03-16 10:17:52 UTC Comment hidden (obsolete)
Comment 136 Martin Renvoize (ashimema) 2026-03-16 10:17:54 UTC Comment hidden (obsolete)
Comment 137 Martin Renvoize (ashimema) 2026-03-16 10:17:56 UTC Comment hidden (obsolete)
Comment 138 Martin Renvoize (ashimema) 2026-03-16 10:17:57 UTC Comment hidden (obsolete)
Comment 139 Martin Renvoize (ashimema) 2026-03-16 10:17:59 UTC Comment hidden (obsolete)
Comment 140 Martin Renvoize (ashimema) 2026-03-16 10:18:01 UTC Comment hidden (obsolete)
Comment 141 Martin Renvoize (ashimema) 2026-03-16 10:18:02 UTC Comment hidden (obsolete)
Comment 142 Martin Renvoize (ashimema) 2026-03-16 10:18:04 UTC Comment hidden (obsolete)
Comment 143 Martin Renvoize (ashimema) 2026-06-04 16:15:53 UTC Comment hidden (obsolete)
Comment 144 Martin Renvoize (ashimema) 2026-06-04 16:15:54 UTC Comment hidden (obsolete)
Comment 145 Martin Renvoize (ashimema) 2026-06-04 16:15:56 UTC Comment hidden (obsolete)
Comment 146 Martin Renvoize (ashimema) 2026-06-04 16:15:57 UTC Comment hidden (obsolete)
Comment 147 Martin Renvoize (ashimema) 2026-06-04 16:15:59 UTC Comment hidden (obsolete)
Comment 148 Martin Renvoize (ashimema) 2026-06-04 16:16:00 UTC Comment hidden (obsolete)
Comment 149 Martin Renvoize (ashimema) 2026-06-04 16:16:02 UTC Comment hidden (obsolete)
Comment 150 Martin Renvoize (ashimema) 2026-06-04 16:16:04 UTC Comment hidden (obsolete)
Comment 151 Martin Renvoize (ashimema) 2026-06-04 16:16:05 UTC Comment hidden (obsolete)
Comment 152 Martin Renvoize (ashimema) 2026-06-04 16:16:07 UTC Comment hidden (obsolete)
Comment 153 Martin Renvoize (ashimema) 2026-06-04 16:16:08 UTC Comment hidden (obsolete)
Comment 154 Martin Renvoize (ashimema) 2026-06-04 16:16:10 UTC Comment hidden (obsolete)
Comment 155 Martin Renvoize (ashimema) 2026-06-04 16:16:12 UTC Comment hidden (obsolete)
Comment 156 Martin Renvoize (ashimema) 2026-06-04 16:16:13 UTC Comment hidden (obsolete)
Comment 157 Martin Renvoize (ashimema) 2026-06-04 16:16:15 UTC Comment hidden (obsolete)
Comment 158 Martin Renvoize (ashimema) 2026-06-04 17:22:16 UTC Comment hidden (obsolete)
Comment 159 Martin Renvoize (ashimema) 2026-06-04 17:22:17 UTC Comment hidden (obsolete)
Comment 160 Martin Renvoize (ashimema) 2026-06-04 17:22:19 UTC Comment hidden (obsolete)
Comment 161 Martin Renvoize (ashimema) 2026-06-04 17:22:21 UTC Comment hidden (obsolete)
Comment 162 Martin Renvoize (ashimema) 2026-06-04 17:22:22 UTC Comment hidden (obsolete)
Comment 163 Martin Renvoize (ashimema) 2026-06-04 17:22:24 UTC Comment hidden (obsolete)
Comment 164 Martin Renvoize (ashimema) 2026-06-04 17:22:25 UTC Comment hidden (obsolete)
Comment 165 Martin Renvoize (ashimema) 2026-06-04 17:22:27 UTC Comment hidden (obsolete)
Comment 166 Martin Renvoize (ashimema) 2026-06-04 17:22:28 UTC Comment hidden (obsolete)
Comment 167 Martin Renvoize (ashimema) 2026-06-04 17:22:30 UTC Comment hidden (obsolete)
Comment 168 Martin Renvoize (ashimema) 2026-06-04 17:22:32 UTC Comment hidden (obsolete)
Comment 169 Martin Renvoize (ashimema) 2026-06-04 17:22:33 UTC Comment hidden (obsolete)
Comment 170 Martin Renvoize (ashimema) 2026-06-04 17:22:35 UTC Comment hidden (obsolete)
Comment 171 Martin Renvoize (ashimema) 2026-06-04 17:22:37 UTC Comment hidden (obsolete)
Comment 172 Martin Renvoize (ashimema) 2026-06-04 17:22:38 UTC Comment hidden (obsolete)
Comment 173 Martin Renvoize (ashimema) 2026-06-04 17:22:40 UTC Comment hidden (obsolete)
Comment 174 Martin Renvoize (ashimema) 2026-06-04 17:22:41 UTC Comment hidden (obsolete)
Comment 175 Martin Renvoize (ashimema) 2026-06-04 17:30:29 UTC
Created attachment 199971 [details] [review]
Bug 35104: Throw exception on store of invalid marcxml

This patch adds an exception Koha::Biblio::Metadata->store to prevent
erroneous characters in marcxml being stored into the database.

We also remove the cleanup of such characters from TransformHtmlToMarc
as introduced by bug 34549.

This should result in there being no way, outside of direct database
manipulation, of getting forbidden characters into our marcxml store.

We'll need to work through the codebase to catch the exception thrown to
give the end user the oportunities to act on the error and fix the data
at source.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Comment 176 Martin Renvoize (ashimema) 2026-06-04 17:30:30 UTC
Created attachment 199972 [details] [review]
Bug 35104: Catch the failure on new and edited records

We now catch the error and display it to the end user so they can fix
the issues before attempting to save again.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Comment 177 Martin Renvoize (ashimema) 2026-06-04 17:30:32 UTC
Created attachment 199973 [details] [review]
Bug 35104: Catch the failure on merging records

We now catch the error and display it to the end user so they can fix
the issues before attempting to merge again.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Comment 178 Martin Renvoize (ashimema) 2026-06-04 17:30:33 UTC
Created attachment 199974 [details] [review]
Bug 35104: Add unit tests for exception on store

We add a new set of unit tests for the exception we introduce here, and
we also update the existing tests to directly manipulate data in the
database rather then using AddBiblio which will now fail due to the
introduced exception on attempting to add bad data.

Sponsored-by: OpenFifth
Comment 179 Martin Renvoize (ashimema) 2026-06-04 17:30:35 UTC
Created attachment 199975 [details] [review]
Bug 35104: Add biblio_metadata_errors table

The preceding signed-off commits (69a6b1c09f7..bfb14bb949e) established an
approach where Metadata::store throws a Koha::Exceptions::Metadata::Invalid
exception when non-XML characters are encountered, requiring every caller to
catch and handle the exception explicitly.

Building on that foundation, we change direction here: instead of throwing,
Metadata::store will strip non-XML characters gracefully on save and persist
structured error details in a new biblio_metadata_errors table.  This will give
cataloguers a clear view of which fields were affected, with context snippets
pointing to the exact character positions, and allows them to review and
confirm or correct the data before marking the errors as resolved.

The new biblio_metadata_errors table supports multiple per-field error records
per metadata row.

Sponsored-by: OpenFifth
Comment 180 Martin Renvoize (ashimema) 2026-06-04 17:30:37 UTC Comment hidden (obsolete)
Comment 181 Martin Renvoize (ashimema) 2026-06-04 17:30:38 UTC Comment hidden (obsolete)
Comment 182 Martin Renvoize (ashimema) 2026-06-04 17:30:40 UTC Comment hidden (obsolete)
Comment 183 Martin Renvoize (ashimema) 2026-06-04 17:30:41 UTC
Created attachment 199979 [details] [review]
Bug 35104: Strip non-XML characters gracefully with per-field error tracking

Rewrite Metadata::store to strip non-XML characters automatically rather
than throwing a Koha::Exceptions::Metadata::Invalid exception.  When
stripping is needed:

* Every bad character is located individually via _find_nonxml_chars(),
  which scans the raw MARCXML and records the field reference (e.g.
  336$a), character ordinal, 1-based position within the subfield value,
  and a two-line context snippet generated by _context_snippet().

* Context snippets show up to 30 characters either side of the bad
  character, replacing it with the appropriate Unicode Control Picture
  (U+2400-U+241F for C0 controls, U+2421 for DEL, U+FFFD otherwise) so
  the location is visible even in plain text.  A second line carries a
  caret (^) aligned beneath the replacement glyph.

* Each occurrence is stored as a separate row in biblio_metadata_errors
  with error_type 'nonxml_stripped'.  On a clean re-save the existing
  error rows are deliberately left in place: they are review flags
  requiring explicit human resolution and must not be silently cleared.
  Only a save that triggers fresh stripping replaces the existing set.

* A new stripped_on_last_store() method lets callers distinguish between
  "stripping just happened" and "pre-existing errors are present", so
  the UI can avoid spuriously re-displaying a save-time warning when the
  record is simply re-saved without changes.

If the MARCXML cannot be recovered at all even after stripping, the
existing Koha::Exceptions::Metadata::Invalid exception is still thrown.

Sponsored-by: OpenFifth
Comment 184 Martin Renvoize (ashimema) 2026-06-04 17:30:43 UTC
Created attachment 199980 [details] [review]
Bug 35104: Strip non-XML characters gracefully and notify user on save

Wire the new Metadata::store graceful stripping through every path that
saves a bibliographic record:

* C4::Biblio::ModBiblioMarc collects stripping warnings via an optional
  warnings arrayref and pushes a 'nonxml_stripped' entry (using the new
  stripped_on_last_store() flag so pre-existing errors are not mistaken
  for a fresh stripping event).  TransformHtmlToMarc no longer pre-empts
  the store-level check by stripping silently on its own.

* cataloguing/addbiblio.pl handles the NONXML_STRIPPED warning: when
  stripping occurred the record is shown back in the editor together with
  a prominent "Record saved with modifications" notice so the cataloguer
  can review the result before continuing.

* cataloguing/merge.pl propagates the same pattern for merge operations.

* Koha::Import::Record::process() catches the Metadata::Invalid exception
  that is still thrown for truly unrecoverable MARCXML.

* misc/migration_tools/bulkmarcimport.pl logs a warning when stripping
  occurs during a bulk import.

Sponsored-by: OpenFifth
Comment 185 Martin Renvoize (ashimema) 2026-06-04 17:30:45 UTC
Created attachment 199981 [details] [review]
Bug 35104: Display non-XML stripping warnings on biblio detail pages

When a bibliographic record has nonxml_stripped errors recorded in
biblio_metadata_errors, display a warning banner on both the normal
detail page (catalogue/detail.pl) and the MARC detail page
(catalogue/MARCdetail.pl) so that staff are aware the stored record may
differ from the original data.

Sponsored-by: OpenFifth
Comment 186 Martin Renvoize (ashimema) 2026-06-04 17:30:46 UTC
Created attachment 199982 [details] [review]
Bug 35104: Show nonxml_stripped errors in edit form with quick-links to fields

When a biblio being edited has existing nonxml_stripped error rows in
biblio_metadata_errors, display them in a 'Data quality warnings' alert
banner above the MARC editor.  Each error entry includes a 'Go to field'
quick-link that switches to the correct tab and scrolls to the first
occurrence of the affected subfield so the cataloguer can inspect and
correct the data.

Sponsored-by: OpenFifth
Comment 187 Martin Renvoize (ashimema) 2026-06-04 17:30:48 UTC
Created attachment 199983 [details] [review]
Bug 35104: Add 'Mark resolved' button for nonxml_stripped errors in edit form

Extend the data quality warnings banner in the MARC editor with a
per-error 'Mark resolved' button.  Clicking it calls a new REST endpoint
(DELETE /api/v1/biblios/{biblio_id}/metadata/errors/{error_id}) which
removes the corresponding biblio_metadata_errors row.  The button is then
hidden client-side to give immediate feedback.

The new Koha::REST::V1::Biblios::MetadataErrors controller handles the
delete, verifying that the requested error belongs to the requested biblio
before removing it.

Sponsored-by: OpenFifth
Comment 188 Martin Renvoize (ashimema) 2026-06-04 17:30:49 UTC
Created attachment 199984 [details] [review]
Bug 35104: Update and extend tests to reflect graceful non-XML stripping

* t/db_dependent/Koha/Biblio/Metadata.t: replace tests that expected an
  exception on bad MARCXML with tests that verify graceful stripping
  behaviour, per-field error recording, context snippet format, the
  stripped_on_last_store flag, and preservation of existing error rows on
  a clean re-save.

* t/db_dependent/Biblio.t: remove tests that relied on the exception path
  and update assertions to match the graceful-stripping model.

* t/db_dependent/Exporter/Record.t: bypass store() validation for a test
  record that intentionally contains bad data so the export-path tests
  remain independent of the stripping logic.

Sponsored-by: OpenFifth
Comment 189 Martin Renvoize (ashimema) 2026-06-04 17:30:51 UTC
Created attachment 199985 [details] [review]
Bug 35104: (follow-up) Tests should be executable
Comment 190 Martin Renvoize (ashimema) 2026-06-04 17:30:52 UTC
Created attachment 199986 [details] [review]
Bug 35104: (follow-up) QA fixes

- Use say_success() in atomicupdate instead of bare say $out
- Log a warning in Koha::Import::Record::get_marc_record when both
  XML parse attempts fail, preserving the original error context
- Add plan tests to the three inner subtests that were missing them
- Eliminate double DB query (count + as_list) in catalogue/detail.pl
  and catalogue/MARCdetail.pl
- Report all nonxml_stripped occurrences in the ModBiblioMarc warning
  message, not just the first
Comment 191 Martin Renvoize (ashimema) 2026-06-04 17:30:54 UTC
Created attachment 199987 [details] [review]
Bug 35104: (follow-up) Fix QA issues

- Run perltidy on C4/Biblio.pm and Koha/Import/Record.pm
- Replace raw MARCXML heredoc in test with MARC::Record->as_xml_record
  to avoid forbidden http:// URL pattern
- Update POD example in Koha::Biblio::Metadata to avoid codespell false
  positive on word fragment
Comment 192 Martin Renvoize (ashimema) 2026-06-04 17:30:56 UTC
Created attachment 199988 [details] [review]
Bug 35104: (follow-up) Simplify encode_error control flow in addbiblio.pl

Handle the encode_error and nonxml_stripped cases with early exits
immediately after the try/catch block. This removes the need for
!$encode_error guards on all the success-path branches, as suggested
by David Cook in comment #85.
Comment 193 David Cook 2026-06-19 06:38:53 UTC
Oops I didn't realise I was the QA contact on this. Looks like I'll have to take a look next week...
Comment 194 Nick Clemens (kidclamp) 2026-07-07 14:07:50 UTC
Could we get a test plan here, Martin?

I tried exporting record 369 - but we can't do that because if bad xml characters, so then I copied the marcxml from the DB and saved it into a file

Before the patches:
I can stage and import the record - I cannot view or edit the record

After the patches:
I can stage the record, import fails with no visible error in staff side,logs show:
Invalid data, cannot decode metadata object (biblio_metadata.id=, biblionumber=441, format=marcxml, schema=MARC21, decoding_error='Field 942 must have at least one subfield at /usr/share/perl5/MARC/File/XML.pm line 483.') at /kohadevbox/koha/Koha/BackgroundJob/MARCImportCommitBatch.pm line 93.
Use of uninitialized value in sprintf at /kohadevbox/koha/Koha/Exceptions/Metadata.pm line 41.




I also tried editing the existing record 369 - with or without the patches I require bug 42512

Before the patches:
I get the warning about the record on details
I can edit and fix

After the patches:
I get the warning about the record on details
I get a warning about the record on edit
I can edit and fix


I think importing needs to provide the error to the user if it is going to fail on bad encoding
Comment 195 Martin Renvoize (ashimema) 2026-07-07 18:05:51 UTC
Test plan
=========
Prerequisites:
- Bug 42512 applied (needed to edit records whose metadata cannot be fully decoded).
- A record with MARCXML containing invalid/non-recoverable content. Two useful variants:
  a) Non-XML characters: a control character embedded in a field value (this is the original,
     "characters invalid in XML" scenario the bug targets).
  b) A structurally invalid field: a <datafield> element with no <subfield> children at all,
     e.g. `<datafield tag="500" ind1=" " ind2=" "></datafield>`. MARC::File::XML refuses to parse
     this ("Field NNN must have at least one subfield"). Legacy/externally-sourced records can
     contain this even though Koha's own live code cannot produce it directly.
  You can inject either into an existing record's stored metadata via the DB to set up test data:
     UPDATE biblio_metadata SET metadata = REPLACE(metadata, '</record>',
       '<datafield tag="500" ind1=" " ind2=" "></datafield></record>')
     WHERE biblionumber = <some biblionumber>;
1) Single record view/edit - non-XML characters (existing behaviour)
   - Save a record containing an invalid-for-XML character in a field.
   - Confirm the character is stripped automatically, an error is recorded and shown on the
     detail/edit pages with a quick-link to the affected field, and a "Mark resolved" button is
     available to dismiss it once corrected.
2) Single record view/edit - empty datafield (new recovery)
   - Corrupt biblio_metadata for an existing record with an empty <datafield> as above.
   - View the record's detail page: a "Record was automatically modified" warning is shown,
     listing the field that was removed (e.g. "500: field removed, it had no subfields").
   - Edit the record (cataloguing/addbiblio.pl): the same warning appears in a "Data quality
     warnings" banner. Unlike the non-XML-character case, there is no "Go to field" link (the
     whole field was removed, so there's nothing left in the record to jump to) - only the
     message and a "Mark resolved" button.
   - Confirm a record with BOTH an empty datafield and a non-XML character in different fields
     shows both warnings together, and both can be independently marked resolved.
3) Batch import of an unrecoverable record
   - Stage a MARCXML file containing one record with a non-recoverable metadata problem, together
     with one or more normal, valid records in the same batch (Tools > Stage MARC records for
     import).
   - Complete staging, then commit the batch (Tools > Manage staged MARC records > Import this
     batch).
   - Confirm:
     - The background job finishes (does not silently fail with no detail).
     - The job's report shows a "Number of records with errors" count of 1, alongside the normal
       added/updated/ignored counts.
     - The valid record(s) in the same batch are still imported successfully.
     - On "Manage staged MARC records", the bad record's row shows a red "Error" status; hovering
       over it shows the decoding error message.
4) Reverting a batch with an unrecoverable record
   - Import a batch that overlays (replaces) an existing record, then corrupt that record's
     matched biblio's metadata via the DB so a subsequent revert cannot decode the pre-overlay
     copy stored in marcxml_old (or use a batch where the original record already had this issue).
   - Revert the batch (Tools > Manage staged MARC records > Revert this batch).
   - Confirm the revert job finishes rather than failing outright, the affected record is left
     with an "error" status, and other records in the batch are still reverted normally.
5) CLI import (misc/commit_file.pl)
   - Stage the same batch as in (3), then run: misc/commit_file.pl --batch-number <id>
   - Confirm the printed summary includes a "Number of records with errors" line, and the command
     completes rather than dying.
6) Maintenance scripts
   - Run misc/maintenance/touch_all_biblios.pl and misc/maintenance/sanitize_records.pl against a
     range of biblios that includes one with unrecoverable metadata.
   - Confirm each script reports the error for that one biblionumber and continues processing the
     rest, rather than dying partway through.
7) Regression check
   - Repeat a normal batch import and a normal single-record edit (no encoding problems) and
     confirm behaviour, counts, and displayed messages are unchanged from before this patch.
Automated tests:
   - t/db_dependent/ImportBatch.t
   - t/db_dependent/Koha/BackgroundJob/MARCImportCommitBatch.t
   - t/db_dependent/Koha/BackgroundJob/MARCImportRevertBatch.t
   - t/db_dependent/Koha/Biblio/Metadata.t
   - t/db_dependent/Biblio.t
   - t/db_dependent/api/v1/biblios_metadata_errors.t
Comment 196 Martin Renvoize (ashimema) 2026-07-07 18:06:01 UTC
Created attachment 201672 [details] [review]
Bug 35104: (follow-up) Surface invalid metadata errors during batch import

QA testing showed that importing a staged record whose MARCXML cannot
be decoded (and cannot be recovered by non-XML character stripping) now
throws Koha::Exceptions::Metadata::Invalid from Koha::Biblio::Metadata::store,
but C4::ImportBatch::BatchCommitRecords had no handling for it. The
exception propagated out of the whole batch commit job, silently marking
the entire job as failed with no way for staff to see which record or
why - only visible in the server logs.

BatchCommitRecords now catches this specific exception per-record,
rolls back that record's changes, flags it with status 'error' and
records the decoding error in import_records.import_error, and
continues committing the rest of the batch. The new count is reported
back through Koha::BackgroundJob::MARCImportCommitBatch's job report
and misc/commit_file.pl's summary, and the per-record error message is
now shown in the "Manage staged MARC records" table.

Also fixes a "Use of uninitialized value in sprintf" warning in
Koha::Exceptions::Metadata::full_message, hit every time this exception
is raised for metadata that has not yet been stored (no id yet).
Comment 197 Martin Renvoize (ashimema) 2026-07-07 18:06:03 UTC
Created attachment 201673 [details] [review]
Bug 35104: (follow-up) Isolate metadata decode failures in other batch loops

Proactively applies the same fix as the previous follow-up to other
places that loop over multiple records and can hit an undecodable
MARC record along the way:

- C4::ImportBatch::_batchCommitItems: an item whose MARCXML cannot be
  decoded now uses the existing import_items error/status columns
  (as already done for duplicate barcodes) instead of dying and
  aborting the rest of the record's items.
- C4::ImportBatch::BatchRevertRecords: a record whose marcxml_old
  cannot be decoded during a 'restore' revert is now flagged with
  status 'error' and counted in num_errors instead of aborting the
  whole revert job. Koha::BackgroundJob::MARCImportRevertBatch's
  progress count is updated to include num_errors.
- misc/maintenance/touch_all_biblios.pl and
  misc/maintenance/sanitize_records.pl: both call
  $biblio->metadata->record without catching the exception it throws
  on undecodable metadata (sanitize_records.pl even had a dead
  "unless ($record)" check, since record() throws rather than
  returning false). One corrupt record would silently stop either
  script partway through a potentially large run. Both now report the
  error for that biblionumber and continue with the rest.
Comment 198 Martin Renvoize (ashimema) 2026-07-07 18:06:06 UTC
Created attachment 201674 [details] [review]
Bug 35104: (follow-up) Recover records with an empty datafield, generalize warning UI

MARC::File::XML refuses to parse a <datafield> with no <subfield>
children at all ("Field NNN must have at least one subfield"), which
happens with legacy data saved before Koha validated MARCXML on
write. Previously this was only caught by the "truly unrecoverable"
branch of Koha::Biblio::Metadata::store, throwing
Koha::Exceptions::Metadata::Invalid.

store() now detects this specific fault the same way it already
detects non-XML characters, and recovers by removing the empty
datafield (its data cannot be reconstructed, so a
'empty_datafield_stripped' error row is written to
biblio_metadata_errors for a cataloguer to review, exactly like the
existing 'nonxml_stripped' rows). Both faults are detected
independently, so a record with both is repaired in one pass and both
error types are recorded.

The nonxml_stripped-specific plumbing that surfaces these rows to
staff (detail/MARCdetail warning banners, the addbiblio.pl edit-form
banner with quick-links and "Mark resolved", merge.pl, and
bulkmarcimport.pl's log output) is generalized to show any recorded
metadata error rather than hardcoding one error_type, so future
recoverable-fault types need no further UI plumbing. The "go to
field" quick-link is only offered for faults that leave the field in
place to link to (nonxml_stripped); empty_datafield_stripped removes
the whole field, so there's nothing left to link to.

C4::Biblio::ModBiblioMarc's warnings-array forwarding is likewise
generalized: Koha::Biblio::Metadata::store now puts an
already-formatted message string in the object_message payload
(instead of a raw occurrence array plus type-specific formatting
logic living in C4::Biblio.pm), so ModBiblioMarc can forward any
warning-type message without knowing its shape. The final message
text sent to callers is unchanged, verified by the existing
nonxml_stripped tests in t/db_dependent/Biblio.t passing unmodified.
Comment 199 David Cook 2026-07-08 06:25:42 UTC
37 files changed, 1731 insertions(+), 145 deletions(-)

That's a bit intimidating to test in one go... 

I'll give up QA on this one, and see what other bugs I can QA more readily...
Comment 200 Martin Renvoize (ashimema) 2026-07-08 12:04:23 UTC Comment hidden (obsolete)
Comment 201 Martin Renvoize (ashimema) 2026-07-08 12:04:25 UTC Comment hidden (obsolete)
Comment 202 Martin Renvoize (ashimema) 2026-07-08 12:04:26 UTC Comment hidden (obsolete)
Comment 203 Martin Renvoize (ashimema) 2026-07-08 12:04:28 UTC Comment hidden (obsolete)
Comment 204 Martin Renvoize (ashimema) 2026-07-08 12:04:29 UTC Comment hidden (obsolete)
Comment 205 Martin Renvoize (ashimema) 2026-07-08 12:04:31 UTC Comment hidden (obsolete)
Comment 206 Martin Renvoize (ashimema) 2026-07-08 12:04:33 UTC Comment hidden (obsolete)
Comment 207 Martin Renvoize (ashimema) 2026-07-08 12:04:34 UTC Comment hidden (obsolete)
Comment 208 Martin Renvoize (ashimema) 2026-07-08 12:24:28 UTC Comment hidden (important, obsolete)
Comment 209 Martin Renvoize (ashimema) 2026-07-08 12:24:30 UTC Comment hidden (important, obsolete)
Comment 210 Martin Renvoize (ashimema) 2026-07-08 12:24:31 UTC Comment hidden (important, obsolete)
Comment 211 Martin Renvoize (ashimema) 2026-07-08 12:24:33 UTC Comment hidden (important, obsolete)
Comment 212 Martin Renvoize (ashimema) 2026-07-08 12:24:34 UTC Comment hidden (important, obsolete)
Comment 213 Martin Renvoize (ashimema) 2026-07-08 12:24:36 UTC Comment hidden (important, obsolete)
Comment 214 Martin Renvoize (ashimema) 2026-07-08 12:24:38 UTC Comment hidden (important, obsolete)
Comment 215 Martin Renvoize (ashimema) 2026-07-08 12:24:39 UTC Comment hidden (important, obsolete)
Comment 216 Martin Renvoize (ashimema) 2026-07-08 12:53:08 UTC
I split this into multiple bugs to make testing and QA simpler.. the final state when all bugs are applied is the same as it was before I did the split so I kept the original signoff attributions.
Comment 217 David Cook 2026-07-15 06:37:29 UTC
*** Bug 42413 has been marked as a duplicate of this bug. ***
Comment 218 Nick Clemens (kidclamp) 2026-08-06 11:40:13 UTC
Created attachment 203259 [details] [review]
Bug 35104: Add biblio_metadata_errors table for per-fault error tracking

Adds biblio_metadata_errors to record individual faults (non-XML
characters, empty datafields, ...) found and recovered when saving a
bibliographic record's MARCXML, so they can be surfaced to
cataloguers and explicitly resolved once reviewed.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 219 Nick Clemens (kidclamp) 2026-08-06 11:40:15 UTC
Created attachment 203260 [details] [review]
Bug 35104: Automated Schema Update

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 220 Nick Clemens (kidclamp) 2026-08-06 11:40:17 UTC
Created attachment 203261 [details] [review]
Bug 35104: Add koha_object_class to BiblioMetadataError schema result

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 221 Nick Clemens (kidclamp) 2026-08-06 11:40:19 UTC
Created attachment 203262 [details] [review]
Bug 35104: Guard Koha::Exceptions::Metadata::Invalid::full_message against undef fields

full_message() interpolated id/biblionumber/format/schema/decoding_error
directly into a sprintf without checking definedness, producing Perl
warnings whenever one of them is unset (e.g. a new, not-yet-stored
metadata object with no id yet).

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 222 Nick Clemens (kidclamp) 2026-08-06 11:40:20 UTC
Created attachment 203263 [details] [review]
Bug 35104: Add Koha::Biblio::Metadata::Error(s) object classes

Thin Koha::Object(s) wrappers around biblio_metadata_errors. error_type
is restricted to a known set of constants, validated on store(), so a
typo'd or unknown value fails loudly rather than writing an
unrecognised value into the database. Errors::by_type() gives callers
a named filter instead of hand-rolling the same search().

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 223 Nick Clemens (kidclamp) 2026-08-06 11:40:22 UTC
Created attachment 203264 [details] [review]
Bug 35104: Recover MARCXML that fails to decode, or throw when unrecoverable

Koha::Biblio::Metadata->store() now validates that MARCXML round-trips
through MARC::Record before saving it, rather than saving whatever was
given and letting corruption surface later as an opaque failure
somewhere downstream.

Two faults are recovered automatically, and either or both may apply
to the same record:

- non-XML characters (recorded as nonxml_stripped, recovered via
  StripNonXmlChars)
- datafields with no subfields at all, which MARC::File::XML refuses
  to parse (recorded as empty_datafield_stripped, recovered by
  removing the empty datafield)

Each recovered fault is logged, reported via object_messages for the
caller to act on, and recorded as a biblio_metadata_errors row (tag
and subfield included where identifiable) for later review - except
where an identical row already exists for the same metadata_id, so a
source record that stays dirty across repeated saves (e.g. re-harvested
from an external repository) doesn't pile up duplicate rows for the
same fault. Existing rows are otherwise left alone on a clean save,
since they're review flags requiring explicit resolution, not a cache
to invalidate.

If the MARCXML cannot be recovered at all, Koha::Exceptions::Metadata::Invalid
is thrown. The recovery logic is exposed as a repair_marcxml class
method so other callers that parse MARCXML ahead of a store() (see
following bugs) can reuse it instead of re-implementing it.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 224 Nick Clemens (kidclamp) 2026-08-06 11:40:24 UTC
Created attachment 203265 [details] [review]
Bug 35104: Catch invalid metadata and surface repair warnings in AddBiblio/ModBiblio

AddBiblio/ModBiblio/ModBiblioMarc gain an optional warnings arrayref
option: any repair messages recorded by Koha::Biblio::Metadata->store()
(see previous commit) are appended to it via object_messages, so a
caller can show the cataloguer what was automatically fixed instead of
the fix happening invisibly.

AddBiblio also now distinguishes Koha::Exceptions::Metadata::Invalid
from other errors in its catch block and rethrows it, so a caller can
tell "this record is unrecoverable" apart from an unexpected internal
error and react accordingly (see following bugs for callers doing so).

The inline StripNonXmlChars calls in TransformHtmlToMarc are removed,
since stripping is now handled centrally and consistently by
Koha::Biblio::Metadata->store() rather than ad-hoc at every call site.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 225 Nick Clemens (kidclamp) 2026-08-06 11:40:26 UTC
Created attachment 203266 [details] [review]
Bug 35104: Update Exporter/Record.t fixture for the new store() validation

This test built a "bad" biblio by storing invalid MARCXML directly via
Koha::Biblio::Metadata->store(), which now rejects it. Simulate
legacy corrupt data already in the database by writing it straight to
the DBIC row, bypassing the new validation, instead.

Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Comment 226 Nick Clemens (kidclamp) 2026-08-06 11:53:38 UTC
Initially 
sub _find_nonxml_chars 
sub _find_empty_datafields
sub _context_snippet

Seemed like additional work as MARC::Record::new_from_xml returns an error, however, it will only return the first error, and not all
Comment 227 Nick Clemens (kidclamp) 2026-08-06 11:54:45 UTC
*** Bug 42528 has been marked as a duplicate of this bug. ***
Comment 228 Pedro Amorim (ammopt) 2026-08-12 15:45:39 UTC
Thanks everyone! Pushed to main for 26.11!
Comment 229 David Cook 2026-08-13 03:33:15 UTC
(In reply to Pedro Amorim (ammopt) from comment #228)
> Thanks everyone! Pushed to main for 26.11!

Oh great! Thanks to everyone for working on this one. My apologies for not being able to contribute as much as I wanted to here.
Comment 230 Pedro Amorim (ammopt) 2026-08-13 10:11:25 UTC Comment hidden (obsolete)
Comment 231 Pedro Amorim (ammopt) 2026-08-13 10:18:32 UTC
This is causing the following tests to fail:

prove t/db_dependent/Biblio/TransformHtmlToMarc.t
prove t/db_dependent/selenium/regressions.t
Comment 232 Pedro Amorim (ammopt) 2026-08-19 11:02:37 UTC
Created attachment 203782 [details] [review]
Bug 35104: (RM follow-up) Fix regressions.t for Metadata's new auto-repair

Koha::Biblio::Metadata->store() now repairs recoverable MARCXML faults
before saving, so this test's invalid data no longer reaches the DB
and detail.pl never hits a decoding error.

Write the metadata directly via the DBIC row instead, bypassing
store()'s repair, so the test still exercises detail.pl's own
defensive handling of corrupt metadata.

Test plan:
prove t/db_dependent/selenium/regressions.t

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Comment 233 Pedro Amorim (ammopt) 2026-08-19 14:31:23 UTC
Created attachment 203802 [details] [review]
Bug 35104: (RM follow-up) Restore StripNonXmlChars in TransformHtmlToMarc

Removed by 6726b1a2544 on the assumption store() strips centrally -
but TransformHtmlToMarc never calls store(), so callers like
addbiblio.pl (FindDuplicate, duplicate redisplay) see unstripped
control characters again, the same failure Bug 34549 fixed.

repair_marcxml covers more faults but works on full MARCXML and can
throw - too big a change here; left as a follow-up.

Test plan:
prove t/db_dependent/Biblio/TransformHtmlToMarc.t

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>