Your terminal condition is never reached (you're comparing #1 to a space if #1 is a single token, #1 is never a space, or the first two tokens in #1 if there is more than one), hence you get the error and also the additional or. The only reason the recursion stopped is because of the error.
You could do this in the following way instead:
\documentclass{article} \makeatletter \@ifdefinable\question {\def\question#1?{\question@check#1\question@done#1 or\question}} \def\question@check#1\question@done{} \def\question@done#1\question{} \newcommand*\test[1]{\question#1\question@done?} \makeatother \begin{document} \test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?} \end{document}
This works by placing an explicit end marker \question@done as the last element. For each element we process we check whether it is that end-marker. That check simply works by gobbling everything up to the first \question@done token. If #1 doesn't contain that token the explicitly placed \question@done in the replacement text of \question will be gobbled, which leaves #1 or\question in the input stream. I omitted reading #1 #2 #3 as separate arguments, if you need that you'll have to make sure that these spaces are also part of the end marker, which is a bit more complicated, we can achieve that like this:
\documentclass{article} \makeatletter \@ifdefinable\question {% \def\question#1 #2 #3?% {\question@check#3\question@done#1 #2 #3 or\question}% } \def\question@check#1\question@done{} \def\question@done#1\question{} \@ifdefinable\test {\edef\test#1{\noexpand\question#1 \space\noexpand\question@done?}} \makeatother \begin{document} \test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?} \end{document}
which places two consecutive spaces in front of \question@done inside the definition of \test.
With these we still got the problem of the trailing or, because we check for end of recursion only after we placed it. This'll get a bit more tricky if we still want to step through the list one element at a time (or we use @StevenB.Segletes answer, which reads the entire list remainder for each recursion, which is a bit slower, but shouldn't be a show stopper if your lists aren't long anyways).
The approach we take here is to first expand \question twice (so \question and the first \question@check will be expanded). If the list was empty the input will now contain
\question@clean \question@done or \question@done \question
else the input will contain
\question@clean orAb Bc Cd\question
So basically \question@clean should gobble two tokens if those are or, and if the first token is \question@done the input was empty. For this we just reuse \question@check.
\documentclass{article} \makeatletter \@ifdefinable\question {% \edef\question#1 #2 #3?% {% \noexpand\question@check#3\noexpand\question@done \space or#1 #2 #3\noexpand\question }% } \def\question@check#1\question@done{} \def\question@done#1\question{} \def\question@clean#1{\question@check#1\question@done\@gobble} \@ifdefinable\test {% \edef\test#1% {% \unexpanded {\expandafter\expandafter\expandafter\question@clean\question}% #1 \space\noexpand\question@done?% } } \makeatother \begin{document} \test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?} \end{document}

\ifx#1is comparingAtobit's never true if your first letter is upper case and your second is lower case.