Possible bug in schema evolution with TClonesArray


Please read tips for efficient and successful posting and posting code

Describe the bug

I have encountered a potential issue in the case one tries to alter, via pragma instructions in LinkDef, the contetns of a class while reading it from a file if the class is stored as part of a TClonesArray. What we observe is that even if the class is not modified, adding an instruction in the linkdef to access the ‘onfile’ memeber crashes the execution of the program. This seems to happen only if the class in question is stored as part of a TClonesArray (storing it as a part of a vector works OK).

Just to give a bit of context, we are attempting this in order to correct ‘on the fly’ the data stored in a class in a wrong way.
Increasing the classdef and adding/removing other memebers does not help.

Expected behavior

The data member(s) should be modifiable without crashes. It works with std::vector
MWE.zip (2.6 KB)

To Reproduce

Steps to reproduce the behavior:

  1. Unzip MWE.zip and cd to that directory.
  2. $mkdir build && cd build && cmake ../ && make
  3. Run ‘write’ step with ./main 1
  4. Uncomment the pragma lines in Linkdef.hh
  5. Run ‘read’ step with ./main 2

Setup

ROOT v6.38.04
Built for linuxx8664gcc on Mar 12 2026, 21:22:56
From tags/6-38-04@6-38-04
With  std202002
Binary directory: /home/stefan/miniconda3/envs/na62/bin

via conda

Additional context

Edit: This problem does not occur with ROOT v6.32

Thanks for any help with this,
Stefan,
NA62 sw coordinator

Thank you for the report! I can confirm the issue and I moved this to Github: I/O customization rules crash with TClonesArray · Issue #22725 · root-project/root · GitHub

Thanks for the quick reply. Is there any workaround we can use until the patch is available?

A possible workaround is writing an I/O rule for the parent class (class A in the example). This rule would leave target and source empty (set to empty string), so that it operates on the whole object once read from disk. The code section of that rule would then need to loop through the TClonesArray member and fix it up.

Actually, you can also put the the whole class rule directly on Class B (the member of the TClonesArray). Also, you can put the fixup code in a method that you call from the I/O rule. That can make the #pragma statement a little more digestable.

EDIT: you can access the object to be fixed up in the code = "{...}" block through the newObj pointer.

Thank you for the suggestion. I want to confirm that the following #pragma seems to work (at least in the MWE).

#pragma read sourceClass = "ClassB"\
         version = "[1]" \
    source = "" \
    targetClass = "ClassB" \
    target = "" \
    code = "{ newObj->SetField2(newObj->GetField2() + 2);}"

Is this what you meant, @jblomer?

Yes, if this is workable for you, I’d suggest this as a workaround for the time being.