Skip to content

Waters mzML fileContent does not match the spectra actually written #4499

Open

Description

@bspratt

Reader_Waters::fillInMetadata builds fileDescription.fileContent by walking
FunctionIndexList() and translating each function type, independently of what the spectrum
list actually ends up containing. Because that walk is parallel to the real thing rather than
derived from it, the two drift apart in three distinct ways.

Over-declares types that were dropped. createIndex skips SIM, CNL, CNG and - without
srmAsSpectra - SRM functions, but fillInMetadata still advertises them. Reproduce with the
checked-in baseline Reader_Waters_Test.data/160109_Mix1_calcurve_070.mzML, whose <fileContent>
announces MS:1000583 SRM spectrum while the spectrum list holds none (16 MSn spectra, zero SRM).

Under-declares a type that is present. The MSe high-energy reclassification happens
per-spectrum in SpectrumList_Waters::spectrum() (the collision-energy heuristic), not in the
function-type walk, so MSe_Short.mzML contains a spectrum carrying MS:1000580 MSn spectrum and
ms level 2 while <fileContent> lists only MS1 spectrum. To be clear this is an internal
contradiction in the file, not a claim about the acquisition: Waters MSE is all-MS1 with
alternating collision energy, and pwiz chooses to present the high-energy function as MSn so
downstream tools treat it as fragment data. Having made that choice per spectrum, fileContent
should agree. No downstream misbehavior has been demonstrated for this one.

Commits before filtering. fillInMetadata runs inside Reader_Waters::read, before any
wrapping or filtering, so the answer describes the pre-filter list. msconvert with any
spectrum-dropping filter (index, msLevel, scanNumber, polarity) can therefore emit an mzML
whose fileContent advertises a type no surviving spectrum carries.

That last one matters beyond tidiness. PR #4498 makes msconvert declare
MS:1000928 calibration spectrum in fileContent precisely so a consumer can conclude from its
absence that a file has no calibration spectra, and Skyline now relies on that. A filtered output
can currently assert the positive falsely.

Driving the loop from the functions actually represented in the index - and reconciling it with the
per-spectrum reclassification - would fix all three at once, and would remove the need for the
lockmass special case PR #4498 had to add to the same loop.

Activity

  1. chambm commented on Jul 29, 2026

    @chambm
    Member

    This affects basically all vendors. Pwiz allows wrapping spectrum/chromatogram lists in ways that change what fileContent should show, and each wrapping could be different. It'd probably be more trouble/cost than it's worth to reproduce a corrected MSData object in each wrapped object, but it might be feasible to have dynamicFileContent() method that takes a spectrumList and chromatogramList (that could be wrappers) and then figures out the accurate content from those. The hard/costly part would be updating the wrappers to copy fileContent and then remove the terms that are excluded by the wrapper (while the wrapper is building its own index). Could be very slow for some vendors, especially compared to the index filter which is extremely fast.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions