diff --git a/src/game/engine/scenes/headless/claude_player_main.gd b/src/game/engine/scenes/headless/claude_player_main.gd index 1298e44a..0c40d039 100644 --- a/src/game/engine/scenes/headless/claude_player_main.gd +++ b/src/game/engine/scenes/headless/claude_player_main.gd @@ -190,15 +190,23 @@ func _apply_runtime_units_catalog(gs: RefCounted) -> void: # Per-unit file is either a single object or a single-element # array (canonical post-data-architecture migration is array- # rooted; pre-migration is object-rooted). Accept both shapes. - # Rust loader accepts an array; we always ship one. - var parsed_arr: Array = JSON.parse_string(raw) as Array - if parsed_arr != null: + # Rust loader takes an array; we always ship one. + # + # Discriminate by first non-whitespace character of the raw + # JSON: `[` → array shape, `{` → object shape. Avoids the + # strict-typed-cast trap where `as Array` on a Dictionary + # value throws an "Invalid cast" runtime error instead of + # returning null. + var stripped: String = raw.strip_edges() + var head: String = stripped.substr(0, 1) + if head == "[": + var parsed_arr: Array = JSON.parse_string(stripped) as Array for item: Dictionary in parsed_arr: if not item.is_empty(): entries.append(item) - else: - var parsed_obj: Dictionary = JSON.parse_string(raw) as Dictionary - if parsed_obj != null and not parsed_obj.is_empty(): + elif head == "{": + var parsed_obj: Dictionary = JSON.parse_string(stripped) as Dictionary + if not parsed_obj.is_empty(): entries.append(parsed_obj) fname = dir.get_next() dir.list_dir_end()