Any idea why this fails?

<rule id="CATCH_STRANGE" name="" default="off">
    <pattern>
        <token>van</token><token min="0"/><token>van</token>
    </pattern>
    <message>x</message>
    <example type="incorrect">Ik praat <marker>van de ban van</marker> de ring.</example>
</rule>

<token min="0" /> means 0 or 1 tokens, but you have 2 tokens between the "van"s.

But when I added a max=8, it did not work either. Does that require multiple identical words?

No, the fact that it doesn’t work looks like a bug to me.

This way it works:

<rule id="CATCH_STRANGE" name="" default="off">
    <pattern>
        <token>van</token>
        <token min="0" max="3"><exception>van</exception></token>
        <token>van</token>
    </pattern>
    <message>x</message>
    <example type="incorrect">Ik praat <marker>van de ban van</marker> de ring.</example>
</rule>

It works when a value is in the token, but not if there is not. Having an all-catching regexp in the token does not help either.

Hm. Maybe the code checks the first or last ‘van’ as well?

If you don’t put an exception in the repeated tokens, the last “van” is considered one of the repeated tokens. The rule tries to find a final “van”, but it is not found and the rule doesn’t match.

If max=“2”, it works without the exception because 2 is exactly the number of tokens in the sentence.

<rule id="CATCH_STRANGE" name="" default="off">
    <pattern>
        <token>van</token>
        <token min="0" max="2"></token>
        <token>van</token>
    </pattern>
    <message>x</message>
    <example type="incorrect">Ik praat <marker>van de ban van</marker> de ring.</example>
</rule>

Okay. I think it is not what to expect. Bu luckily there is skip= too. I will use that.