> > \usepackage{l3io,xr2} instead and commented out the \RequirePackage.

> actually it works if there is a blank line after the \RequirePackage

This is just a problem because of the current mix of l3 and 2e code.
The rules are that after l3names.cls is read, there are no special
conventions about what comes after \RequirePackage, but before that
the first non-space token after  the } had better be a token whether
read with l3 or 2e conventions.

so after \usepackage{l3io,xr2}
you need a blank line, or \par or [1998/06/01].

The problem is that \usepackage & \RequirePackage look ahead to see if
there is an optional argument coming up. l3names.sty redefines the
lookahead to make sure that @_: are all letters at that point but before
that redefinition has happened, _ is not a letter so if you go

% no blank line here
\tex_def:D .....

then \usepackage looks ahead and tokenises \tex.
The packages run correctly but when they finish, you get an undefined
command \tex followed by an attempt to typeset _def:D.

Probably the clearest way to avoid this is always to Require l3names
first, and on its own


as the second  \RequirePackage is safe to be read under any convention.

There are alternative ways of handling the switching but all the ones I
tried so far have similar bad effects somewhere.

Suggestions accepted...