Skip to content

tracking: audit reward-zero and fake-success failure producers #4883

Open

Description

@yuhezhang-ai

Parent: #3180. Related focused metadata fix: #4882.

Purpose and priority

Audit the original #3180 problem across Gym: failures that are represented as reward-zero results or fabricated successful responses, so callers can confuse missing results with valid wrong answers.

This is a separate follow-up from #4882. The immediate priority is #4882's missing diagnostics on already-masked results; that work does not wait for this audit to finish.

Scope

  • Enumerate all relevant result-producing and result-conversion paths in resources_servers/, responses_api_agents/, environment_servers/, and shared nemo_gym/ helpers at a recorded main revision.
  • Inspect exception handlers, early returns, raw response dictionaries, and adapters. Find synthetic zero rewards, fake/empty successful Responses objects, and infrastructure failures returned as ordinary completed results. Do not limit the search to existing issue numbers or to code that sets mask_sample.
  • Follow each candidate to the final caller-visible result. Record whether there is a valid score, a completed result with an unusable score, or no usable result; retain the available evidence and any unresolved semantic question.
  • Preserve legitimate zero scores as valid task outcomes. Intentional disabled judging and policy-limit cases must not be automatically classified as infrastructure failures.
  • Keep masked failure placeholders in the inventory where a future result-type migration may still be needed, but route missing labels alone to fix: complete failure metadata on existing masked results #4882.
  • Reuse existing environment issues and create/link focused implementation follow-ups under refactor(core): replace reward-zero failure sentinels with typed outcomes #3180 only for uncovered gaps. Existing issues with another parent should be cross-linked without moving that parent.

Inventory deliverable

For each reviewed component, record the path, reviewed revision, failure trigger, current outward result, masking/failure fields, caller interpretation, proposed disposition, linked issue/PR, and any specific dependency.

Record reviewed/no-gap and justified not-applicable cases as well as findings so unreviewed components remain visible. Existing examples are #4571 (Terminal-Bench), #4533 (Indian Banking simulator failure), and the failed-run portion of #4529 (Claude Code); these are not an exhaustive list.

Dependencies and boundaries

The audit and deterministic reproductions can start from main independently of #3302. The shared vocabulary, completed-result fields, and shared failure types have landed through #3213, #2611, and #3877.

Implementation migrations belong in the resulting producer-specific issues. Replacing a successful-looking response with a rewardless failure can change direct callers, replay, persistence, and recovery; each migration must identify the actual support it consumes from #2135 / #3302 and merge that support first.

This audit does not implement a global training include/exclude policy, change reward or masking behavior, or require all environments to migrate in one PR.

Acceptance criteria

Activity

  1. self-assigned this
    on Oct 21, 2026
  2. yuhezhang-ai commented on Oct 30, 2026

    @yuhezhang-ai
    ContributorAuthor

    Inventory: failures scored as results, at main 837ce27ea (Oct 28)

    This is the audit this issue tracks: every place in Gym where a failure (sandbox, judge, verifier, provider, bad task data, harness crash) can be scored as a model result. The fixes fall under #3180; the plan for them is under "Next for #3180" in the #2750 status refresh.

    Full tables: https://gist.github.com/yuhezhang-ai/bec5538e3982f932c8c50848fbdef8e5 (open-findings.csv: the 421 open findings with file and line; components.csv: one line per component). Search them for your component.

    How main treats a result

    • A row with _ng_failure_class goes to the failures file. It isn't scored and can be retried.
    • On the legacy_agent relay, an exception from an agent's /run ends the collection run, unless route_failures_to_sidecar=true turns it into a failures-file row. On an environment server (single_agent_turn, single_agent_turn_legacy), a failed /verify becomes a failure row instead, retried when the error is transient.
    • mask_sample=true keeps the row but leaves it out of scores and the training loss. failure_kind says why.
    • Everything else is scored.

    Results

    All 237 components were reviewed (169 resources servers, 62 agents, 6 environment servers), plus 21 shared nemo_gym/ modules. Each component is counted under its most serious finding:

    Most serious finding Components
    A failure scored as a model result 119
    A failure turned into a successful-looking response 12
    Unclear whose failure it is; needs a decision 40
    Masked without a failure_kind (#4882) 3
    Deliberate, documented zero or mask 14
    Failures handled correctly 10
    Zeros are real wrong answers 33
    No scoring path 6

    So 171 of 237 components have at least one open finding. 12 components mask some failures without a failure_kind; #5223, #5315 and #5342 cover 7 of them.

    Where it matters most

    Of the 122 resources servers with an open finding:

    • 38 ship a training split in their Gym config, so their false zeros reach training rewards. 13 are coding (SWE) environments (r2e_gym, swe_gym, swe_rebench, scale_swe, swe_next, the swemer servers, swerl_gen, swe_pivot, denovo_swe, swe_internal_v1). 25 are judge- or rule-based, such as math_with_judge, instruction_following, mcqa and workplace_assistant. Together they have 97 open failure paths, 59 of which need no decision.
    • 56 are used only by benchmarks.
    • 28 are in neither.

    A training split in Gym's config is a lower bound: any environment can be trained on with other data.

    What goes wrong

    Failure Example Components
    Broken task data is scored 0 mcqa: a row with no expected answer scores 0 41
    A failed agent run reaches the grader as if it were a normal answer codex_agent sends an empty "completed" message after a failed run 36
    Sandbox or grading failures in coding benchmarks are graded as failing tests r2e_gym: the grading command reports a sandbox error without raising, so it is scored wrong and not retried 25
    The scorer itself crashes bigcodebench: the test runner is killed by the out-of-memory killer and scored wrong 22
    Session state is lost before grading newton_bench: session data is missing at grading time 13
    The judge replied, but its verdict is empty or unreadable beam: an empty judge reply scores that rubric item 0 12
    A judge error is caught inside the server and scored 0 text_to_sql: an error in judge bookkeeping returns reward 0 8
    A tool or provider outage is shown to the model as a tool error enterpriseops_gym: tool endpoint server errors reach the model 6
    Failure details are dropped on the way to the output ns_tools, aviary, tau2 3
    A timeout that could be the model's or the machine's 2 clear cases, plus 18 that need a decision 2

    A component can appear in several rows.

    Fixed since a first pass on Oct 23

    How to fix a failure path

    The proposed fix column in open-findings.csv follows one rule, applied to every place a component returns 0 or masks:

    When the grader sees It should
    The model's own failure: a wrong answer, no answer, or the model ran out of its own budget Score it. 0 is the right result.
    A failure the model can't cause, where a retry may help: a sandbox error, a judge outage, a dropped connection Raise a failure with a failure_kind, so Gym records it and can retry it.
    A failure the model can't cause, where a retry won't help: broken task data, judging turned off, a session that no longer exists Mask it with a failure_kind.
    A failure that could be either Follow Q1 below.
    A benchmark that must match an official score Still record the failure, and opt in to counting it as 0 at scoring time (Q6).

    Most fixes are a few lines. Two shared pieces are still in review: #5072 carries a raised failure's kind and reason back to the collector, and #3302 saves failures and retries them on resume.

    Questions to settle

    The table refers to these as Q1–Q6.

    1. If a run fails after the model has started working, whose failure is it? For example, the sandbox dies mid-episode, or the tests hang and the patch may be the cause. Today benchmarks differ: fix(terminal_bench_4): score a verifier that times out or leaves no reward as 0, as Terminal-Bench does #5038 and fix(eval): make opted-in policy timeouts final scored zeros #5220 score some of these as 0, while swebench_pro masks them. Proposal: the environment states the fault when it knows; when it can't tell, the failure is marked unknown and doesn't count against the model by default. For failed agent runs, that default lives in agent_outcome_policy, which today sends a failed response with no outcome to the grader. feat(grpo): add exclude_infra masked-reward policy for infra-masked rollouts RL#4968 takes the same default for training: a masked row counts in GRPO advantages only if its failure_kind is a policy failure such as agent_timeout, agent_limit or model_context_limit. About 60 components.
    2. When a judge replies but its verdict can't be read, is that a judge failure? fix(frontierscience): use paper rubric prompt and reject failed grades #4891 and Fixes to partner_if benchmark, required to pass Frontier Eval quality checks #5007 treat it as one; most judges score it 0. Proposal: retry once, then mask with judge_unparseable. 12 components.
    3. Which failures are worth retrying, and which are final? Today masked rows are never retried, and raised failures are retried by HTTP status, not by cause. Proposal: retry what a new attempt can fix (sandbox errors, judge outages); mask with a reason what it can't (bad task data, judging turned off).
    4. Do all consumers read mask_sample and failure_kind together? A caller that ignores mask_sample sees every masked failure as a real 0. See fix(grpo): keep masked Gym failures out of prompt-group statistics RL#4061, and feat(grpo): add exclude_infra masked-reward policy for infra-masked rollouts RL#4968, which reads failure_kind to keep masked infrastructure failures out of advantages.
    5. When a tool the model uses has an outage mid-episode, should the model see it? For example, a search API returns a server error, and the model sees it as a tool error it can't fix. Proposal: retry inside the tool, then end the rollout as an infrastructure failure.
    6. Benchmarks whose official scoring counts failures as 0. Only 13 of the 252 scored-failure paths look intentional; most are failures the model can't cause, on infrastructure the official harness never ran on, so counting them as 0 makes the score depend on cluster health. Proposal: still record the failure, and let the benchmark opt in to counting it as 0 at scoring time with count_failure_classes_as_zero, as benchmarks/hle_diamond does, noted in its README, and report the failure rate next to the score. That option covers failures-file rows only, so masked rows need the same option keyed by failure_kind. It matters for masked policy failures too: Gym's eval scores leave every masked row out, so a benchmark run through swe_agents doesn't count a run that hit its iteration limit as 0.
  3. giuliolovisotto commented on Oct 30, 2026

    @giuliolovisotto
    Contributor

    @yuhezhang-ai @AdamRajfer I clarified the failure contracts: the direction is configurable OutcomePolicy at each boundary, extending #4834 and the shared OutcomePolicyConfig / choose_action now in #5072. Each boundary supplies documented defaults and supported actions; users/benchmarks can override them through Gym config, with partial overrides preserving the remaining defaults.

    For Yuhe’s questions, I suggest:

    • Q1: retain unknown attribution as unknown; configure the handling action separately, without treating model activity alone as proof of model fault.
    • Q3: configure retry/stop behavior, with bounded retries enforced by the executing component. Stopping retries must not implicitly score zero.
    • Q6: make benchmark-specific zeros an explicit policy choice, preserving the failure evidence.

    We still need to agree on those defaults, but they should be defaults of this shared mechanism rather than independent hard-coded producer rules. Supported actions and context can differ by boundary; accounting/recovery planning still enforces budgets and execution constraints. Q4 remains a consumer-contract requirement, and reporting must retain failed attempts even when retries succeed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions