full_reset() gives no signal whether the environment was reset; skipped reset is indistinguishable from a successful one
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 38/100
Research direction
Read browsergym/webarena/src/browsergym/webarena/instance.py around WebArenaInstance.full_reset() and the three call sites in browsergym/experiments/src/browsergym/experiments/benchmark/utils.py inside prepare_backend(). Compare the skipped and successful reset paths, then decide with maintainers how the result should be surfaced. Done means callers can distinguish a skipped reset from a successful one without changing the current default behavior.
Written by the indexing model from the issue text.
Description
Summary
WebArenaInstance.full_reset() gives the caller no way to tell whether a reset happened.
prepare_backend() calls it with no arguments, so skip_if_not_set=True is always in force. With no
reset endpoint configured, the reset is skipped with a logged warning — but the function then falls
through to the warm-start block and returns None, which is exactly what it returns after a
successful reset. So the benchmark proceeds against an environment that was never reset, and the
caller has no signal to distinguish the two cases. The only evidence is one WARNING line in a long
benchmark log.
This matters because the upstream WebArena documentation treats the reset as required for correct
evaluation:
After evaluating the 812 examples, reset the environment to the initial state
—web-arena-x/webarena, README
All 812 shipped task configs also carry require_reset: false, which is consistent with the intended
protocol being one reset per sweep rather than per task. So the single reset that BrowserGym performs
is the one carrying the reproducibility guarantee — and it is the one that can silently not happen.
Where
browsergym/webarena/src/browsergym/webarena/instance.py:59
def full_reset(self, skip_if_not_set: bool = True):
base_url = os.environ.get(self.RESET_URL_VAR, None)
if not base_url:
logger.error(f"Environment variable {self.RESET_URL_VAR} is missing or empty, required for a full instance reset.")
if skip_if_not_set:
logger.warning("Skipping automated reset. Make sure the instance has been manually reset.")
else:
raise RuntimeError(f"Could not reset instance, aborting.")
else:
... # performs the reset, polls /status until "Ready for duty!"
# warm-start the instance (navigate to every domain) <-- reached in BOTH branches
retries_left = 3
while retries_left:
...
self._check_is_reachable(timeout=60)
Note the fall-through: the warm-start block sits outside the if/else, so the skipped branch does not
return early — and neither branch returns a value that identifies itself.
Called from browsergym/experiments/src/browsergym/experiments/benchmark/utils.py in
prepare_backend(), with no arguments, for all three backends:
| line | backend | reset env var |
|---|---|---|
| 147 | webarena |
WA_FULL_RESET |
| 176 | webarena_verified |
WA_FULL_RESET |
| 205 | visualwebarena |
VWA_FULL_RESET |
(VisualWebArenaInstance subclasses WebArenaInstance and overrides RESET_URL_VAR, so it inherits
the same behaviour.)
Reproduction
No WebArena instance needed — this is the control flow, not the network:
git clone https://github.com/ServiceNow/BrowserGym && cd BrowserGym # verified at 0.14.4, c05d7f3
Every call site uses the silent default:
grep -n "full_reset()" browsergym/experiments/src/browsergym/experiments/benchmark/utils.py
# 147: default_instance.full_reset()
# 176: default_instance.full_reset()
# 205: default_instance.full_reset()
And the branch behaviour, run directly (no live instance needed):
import importlib.util, logging, os
logging.basicConfig(level=logging.INFO)
spec = importlib.util.spec_from_file_location(
"wa_instance", "browsergym/webarena/src/browsergym/webarena/instance.py")
mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)
os.environ.pop("WA_FULL_RESET", None)
inst = mod.WebArenaInstance.__new__(mod.WebArenaInstance) # bypass __init__, needs live env vars
inst.urls = {"probe": "https://example.com"} # stand in for the six sites
print("silent branch returns:", inst.full_reset()) # -> None
inst.full_reset(skip_if_not_set=False) # -> RuntimeError, by contrast
Observed on 0.14.4:
ERROR Environment variable WA_FULL_RESET is missing or empty, required for a full instance reset.
WARNING Skipping automated reset. Make sure the instance has been manually reset.
silent branch returns: None
fail-closed path raises: Could not reset instance, aborting.
A successful reset also returns None — that is the point. The return value carries no information
about whether the environment was reset.
Why I think it is worth changing
The failure is invisible at the point where it matters. A user who has not set WA_FULL_RESET (or
VWA_FULL_RESET) gets a clean run, a normal-looking return value, and results that depend on whatever
state their instance happened to be in. Because the only evidence is one WARNING in a long benchmark
log, the most likely outcome is that nobody notices the reset was skipped — and there is currently no
way for a downstream script to check.
I am not claiming any published WebArena number is wrong. I have not run the benchmark, and the
reset is available to anyone who configures it — this is about the default being silent rather than
about the mechanism being absent.
Suggested fix (policy call, so I'd rather discuss than impose)
Failing closed — full_reset(skip_if_not_set=False) at the three call sites — would break every
current user who has no reset endpoint, so I don't think it should be done unilaterally. Two smaller
options:
- Fail legibly. Have
full_reset()return a bool (or a small enum:reset/skipped), and
haveprepare_backend()surface"environment reset: SKIPPED (WA_FULL_RESET unset)"in the run
header or run metadata, so a run is self-documenting and downstream tooling can assert on it. - Fail closed behind an opt-in. Keep the current default, but add an explicit
WA_REQUIRE_RESET=1that turns the silent skip into a hard error, for anyone who wants the
guarantee enforced.
Either way the current behaviour is preserved by default, so nothing breaks.
Happy to open a PR for whichever you prefer — including just the docs clarification that
WA_FULL_RESET / VWA_FULL_RESET are required for a reproducible run.
- Dominant language
- Python
- Stars
- 1.4k
- Forks
- 202
- Avg merge
- 6d 12h
- Merged PRs (30d)
- 2
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from ServiceNow/BrowserGym
-
[Feature Proposal]: Add experiment lifecycle callbacks to `ExpArgs` in `loop.py`Possibly taken @sriram687 claimed this today. Open
ServiceNow/BrowserGym#417 · 1 assignee ·
-
WebArena-Verified reward averages evaluator scores instead of using the benchmark's task score (partial credit for failed tasks)Possibly taken @salma2vec claimed this 4 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 32/100
ServiceNow/BrowserGym#412 ·
-
AssistantBench scorer treats a thousands separator as a decimal point: "3,080,000" scores 0 against gold 3080000, while "1,000" scores 1.0 against gold 1Possibly taken @Andiii208 claimed this 25 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 25/100
ServiceNow/BrowserGym#404 · 1 comment ·
-
Difficulty 5/5 Over a week Newbie friendliness 28/100
ServiceNow/BrowserGym#401 ·
-
`miniwob.find-greatest` can be marked successful after selecting a non-maximum cardPossibly taken @jstar0 claimed this 102 days ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 25/100
ServiceNow/BrowserGym#392 ·
All issues in ServiceNow/BrowserGym
Similar issues
-
component:benchmark
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
agentic-os-org/ANOLISA#9177 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
distrochooser/distrochooser#520 ·
Maintainers usually reply within 2 days
-
Status: Needs confirmation Status: Needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
Maintainers usually reply within 1 day
-
[BUG] Published Docker images lack the redis extra: HEADROOM_COMPRESS_SESSION_STORE stops startupPossibly taken @AlessandroSpallina claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
headroomlabs-ai/headroom#4513 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 65/100
codespell-project/codespell#4060 ·
Maintainers usually reply within 1 day