Skip to content

Commit dff1380

Browse files
fix: a missing upstream deployment is not a task failure
#53 stopped a dead sandbox being scored as an informative zero. The same masking survives one layer up, for a cause that is even easier to fix. In the 2026-07-25 swe-atlas-qna run, 145 of 469 cases ended on: NotFoundError: Error code: 404 - {'error': {'type': 'invalid_request_error', 'code': 'DeploymentNotFound', 'message': 'The API deployment for this resource does not exist. ...'}} The configured eval model is not provisioned on the Azure resource. The agent's every call 404s, it writes no answer, and `classify_case` — finding nothing in the signal it recognises — returns TASK_FAILURE, whose policy is `is_informative_sample=True`. So each case was recorded as a genuine score of 0.0 and the harness reported that the candidate failed the task. Add UPSTREAM_MODEL_UNAVAILABLE: never an informative sample, never retried, terminating (every remaining case fails identically, so there is nothing left to measure), and counted toward invalidity so the aggregate still comes out invalid if the terminating path is ever bypassed. It is ranked above TRANSIENT_INFRA, so a case that saw both a dead sandbox and a missing deployment reports the deterministic, operator-fixable cause. The pattern deliberately matches the provider error codes and the exact Azure sentence rather than a bare "does not exist": 102 cases in that same run failed with `FetchSpec failed: loading container: file does not exist`, which is genuine infrastructure and must stay TRANSIENT_INFRA. There is a test pinning both sides. Refs #51. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ff0dff8 commit dff1380

2 files changed

Lines changed: 92 additions & 0 deletions

File tree

vero/src/vero/evaluation/error_taxonomy.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ class ErrorCategory(str, Enum):
4343
#: Authentication/authorization failed (e.g. a wrong or cycled key). Does
4444
#: not heal on retry: terminate loudly.
4545
AUTH_FAILURE = "auth_failure"
46+
#: The requested model is not deployed on the configured upstream. A
47+
#: permanent misconfiguration, not the candidate's doing: never scored as a
48+
#: sample, and terminating, because every remaining case will fail the same
49+
#: way and the run has nothing left to measure.
50+
UPSTREAM_MODEL_UNAVAILABLE = "upstream_model_unavailable"
4651
#: Transient external infrastructure (rate limit, timeout, connection, 5xx,
4752
#: overloaded). Retryable; if it persists it renders the aggregate invalid.
4853
TRANSIENT_INFRA = "transient_infra"
@@ -91,6 +96,16 @@ class CategoryPolicy:
9196
is_informative_sample=False,
9297
diagnostic_code="auth_failure",
9398
),
99+
ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE: CategoryPolicy(
100+
retryable=False,
101+
terminating=True,
102+
# Unlike auth, keep counting these toward invalidity: if the terminating
103+
# path is ever bypassed, the aggregate must still come out invalid
104+
# rather than silently averaging a shrinking set of survivors.
105+
counts_toward_invalidity=True,
106+
is_informative_sample=False,
107+
diagnostic_code="upstream_model_unavailable",
108+
),
94109
ErrorCategory.TRANSIENT_INFRA: CategoryPolicy(
95110
retryable=True,
96111
terminating=False,
@@ -149,6 +164,21 @@ def policy(category: ErrorCategory) -> CategoryPolicy:
149164
),
150165
ErrorCategory.AUTH_FAILURE,
151166
),
167+
(
168+
# A model that is configured but not provisioned upstream. Left
169+
# unclassified this is the worst kind of silent failure: the agent's
170+
# every call 404s, it writes no answer, and the case is recorded as an
171+
# informative task failure — the harness blaming the candidate for a
172+
# model that does not exist. Matched on the provider's own error codes
173+
# and on the exact Azure sentence, never on a bare "does not exist",
174+
# which would also swallow "loading container: file does not exist".
175+
re.compile(
176+
r"deploymentnotfound|model_not_found|"
177+
r"the api deployment for this resource does not exist",
178+
re.IGNORECASE,
179+
),
180+
ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE,
181+
),
152182
(
153183
re.compile(
154184
r"rate.?limit|too.?many.?requests|time(?:d.?)?out|connection|"
@@ -205,6 +235,9 @@ def classify_case(signals: list[str]) -> ErrorCategory:
205235
for category in (
206236
ErrorCategory.INFERENCE_BUDGET_EXHAUSTED,
207237
ErrorCategory.AUTH_FAILURE,
238+
# Ahead of infra: a case that saw both a dead sandbox and a missing
239+
# deployment should report the deterministic, operator-fixable one.
240+
ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE,
208241
ErrorCategory.TRANSIENT_INFRA,
209242
):
210243
if category in categories:

vero/tests/test_v05_error_taxonomy.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,62 @@ def test_classify_case_treats_environment_loss_as_infra_not_task_failure():
9595
)
9696
# A candidate's own bug is still an informative task failure, unchanged.
9797
assert classify_case(["ValueError"]) is ErrorCategory.TASK_FAILURE
98+
99+
100+
def test_a_missing_upstream_deployment_is_not_a_task_failure():
101+
"""The 2026-07-25 swe-atlas-qna run's exact terminal exceptions.
102+
103+
145 of its 469 cases died on a model that is not provisioned. Classified as
104+
a task failure, each was recorded as an informative score of 0.0 — the
105+
harness blaming the candidate for a model that does not exist.
106+
"""
107+
azure = (
108+
"Error code: 404 - {'error': {'type': 'invalid_request_error', "
109+
"'code': 'DeploymentNotFound', 'message': 'The API deployment for this "
110+
"resource does not exist. If you created the deployment within the "
111+
"last 5 minutes, please wait a moment and try again.'}}"
112+
)
113+
assert (
114+
classify_signal(azure) is ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE
115+
)
116+
assert (
117+
classify_signal("model_not_found") is ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE
118+
)
119+
assert (
120+
classify_case(["NotFoundError", azure])
121+
is ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE
122+
)
123+
124+
unavailable = policy(ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE)
125+
assert not unavailable.is_informative_sample
126+
assert not unavailable.retryable
127+
assert unavailable.terminating
128+
assert unavailable.counts_toward_invalidity
129+
130+
# It outranks a co-occurring sandbox death: the deterministic,
131+
# operator-fixable cause is the one worth reporting.
132+
assert (
133+
classify_case(
134+
[
135+
"NotFoundError",
136+
"Modal Sandbox with container ID ta-01KY not found.",
137+
azure,
138+
]
139+
)
140+
is ErrorCategory.UPSTREAM_MODEL_UNAVAILABLE
141+
)
142+
143+
144+
def test_missing_deployment_pattern_does_not_swallow_container_load_failures():
145+
"""`FetchSpec failed: loading container: file does not exist` is infra.
146+
147+
It is 102 of that same run's cases, and it also contains "does not exist",
148+
so the deployment pattern must not be written loosely enough to claim it.
149+
"""
150+
fetchspec = "FetchSpec failed: loading container: file does not exist\n"
151+
assert classify_signal(fetchspec) is ErrorCategory.TRANSIENT_INFRA
152+
assert classify_case(["RuntimeError", fetchspec]) is ErrorCategory.TRANSIENT_INFRA
153+
assert (
154+
classify_case(["AddTestsDirError", "Failed to add tests directory to environment."])
155+
is ErrorCategory.TRANSIENT_INFRA
156+
)

0 commit comments

Comments
 (0)