The loss is falling, the generated text is fluent, and the validation loss is suspiciously good. The bug is not in the model: it is in how the training examples were cut.
You will build input-target pairs from a token stream and write the checks that catch the data bugs that raise no errors.