It seems that if substrb finishes part way through a character, it just returns a space:
select substrb('éééééééééé', 1, 3) three_bytes,
dump(substrb('éééééééééé', 1, 3)) three_byte_dump,
ascii(' ') space_chr
from dual;
THR THREE_BYTE_DUMP SPACE_CHR
--- ----------------------- ----------
é Typ=1 Len=3: 195,169,32 32I agree with your conclusion though: the correct solution here is to use character semantics for the column.