This happens because VSCode (or IDEs) expect positions to be calculated using the positionEncoding (see Client capability). pygls has convenient helpers for this, see PositionCodec.

Client and Server has to agree on which encoding they use for positions. Clients report the encodings they support via the positionEncoding field of Client Capability (missing this field means UTF-16 by default). Server has to pick one out of the offered encodings (again missing means UTF-16).