Skip to content

BE: PGN import rejects compact move-number format (1.e4) with a false "Illegal move" #945

Description

@abdulwaarith0

Bug

backend/modules/chess/src/pgn.rsparse_moves() strips move numbers with the regex ^\d+\.+$, which only matches when the move number is its own whitespace-separated token (the spaced form 1. e4).

A compact but perfectly valid PGN where the move number is glued to the move — 1.e4 e5 2.Nf3 Nc6, the form produced by many exporters/engines — tokenizes to ["1.e4", "e5", "2.Nf3", "Nc6"]. The 1.e4 / 2.Nf3 tokens slip through the filter and are then handed to the SAN parser, which rejects them.

Impact

This is live via the JWT-authenticated POST /games/import endpoint (backend/modules/api/src/games.rs:396). Importing a compact-format PGN fails with a 422 "Illegal move" even though the game is completely legal.

Reproduce

[White "A"]
[Black "B"]
[Result "*"]

1.e4 e5 2.Nf3 Nc6 *
  • Compact 1.e4 e5 2.Nf3 Nc6 → parsed ["1.e4","e5","2.Nf3","Nc6"]validate_game = false
  • Spaced 1. e4 e5 2. Nf3 Nc6 → parsed ["e4","e5","Nf3","Nc6"]validate_game = true

Suggested fix

Strip a leading move-number prefix from each token (^\d+\.+, no trailing $ anchor) instead of dropping whole-number-only tokens. This handles 1.e4, 1. e4, 1...e5, and 10.Ba2 uniformly.

Related

While here, parse_moves only removes non-nested variations (\([^()]*\)), so nested variations like (1. d4 (2. c4) Nf6) leave the outer parentheses behind. Noting for a possible follow-up; not addressed here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions