@@ -114,3 +114,39 @@ def test_batch_verify_does_not_mask_validator_import_errors(tmp_path: Path) -> N
114114 assert "missing_verifier_dependency" in result .stderr
115115 assert not reward_out .exists ()
116116 assert not per_query_out .exists ()
117+
118+
119+ def test_batch_verify_isolates_per_query_runtime_validator_error (tmp_path : Path ) -> None :
120+ """A single query's validator raising at call time (e.g. a non-string answer)
121+ must score that query 0 and continue grading the rest — not abort the whole
122+ dataset (which would drop it from the run as a RewardFileNotFoundError)."""
123+ tests_dir = tmp_path / "tests"
124+ tests_dir .mkdir ()
125+ shutil .copy2 (Path (verify_batch_module .__file__ ), tests_dir / "verify_batch.py" )
126+ # q1 validator crashes on a non-string answer; q2 validator is well-behaved.
127+ (tests_dir / "validate_q1.py" ).write_text (
128+ "def validate(answer):\n "
129+ " return (answer.lower() == 'x', 'checked')\n "
130+ )
131+ (tests_dir / "validate_q2.py" ).write_text (
132+ "def validate(answer):\n "
133+ " return (answer == 'ok', 'checked')\n "
134+ )
135+ answers = tmp_path / "answers.json"
136+ answers .write_text (json .dumps ({"q1" : ["a" , "b" ], "q2" : "ok" })) # q1 is a LIST
137+ reward_out = tmp_path / "reward.json"
138+ per_query_out = tmp_path / "reward_per_query.json"
139+
140+ result = _run_generated_verify_batch (
141+ tests_dir = tests_dir ,
142+ answers_path = answers ,
143+ reward_out = reward_out ,
144+ per_query_out = per_query_out ,
145+ )
146+
147+ assert result .returncode == 0 , result .stderr
148+ per_query = json .loads (per_query_out .read_text ())
149+ assert per_query ["q1" ]["reward" ] == 0.0
150+ assert "validator error" in per_query ["q1" ]["reason" ]
151+ assert per_query ["q2" ]["reward" ] == 1.0 # the good query still graded
152+ assert json .loads (reward_out .read_text ()) == {"reward" : 0.5 }
0 commit comments