Repository navigation
fix(experts): report an unsupported avatar delete instead of a 500 - #1297
Open
lihongyuan99 wants to merge 5 commits into
Open
lihongyuan99 wants to merge 5 commits into
lihongyuan99 wants to merge 5 commits into
Conversation
lihongyuan99
force-pushed
the
fix/avatar-delete-unsupported
branch
from
September 29, 2026 23:29
343ec62 to
692f1ef
Compare
Collaborator
|
分诊:问题真实( 请 rebase CHANGELOG 到当前 Unreleased 后再合。CI 已绿。 |
lihongyuan99
force-pushed
the
fix/avatar-delete-unsupported
branch
from
September 30, 2026 15:51
692f1ef to
905fce5
Compare
Contributor
Author
|
Done — Checked against today's CI on |
lihongyuan99
force-pushed
the
fix/avatar-delete-unsupported
branch
3 times, most recently
from
October 5, 2026 12:49
5583835 to
c09c897
Compare
BackendOperationNotSupportedError is a RuntimeError, so the harness signal for a
backend without a delete primitive escaped delete_workspace_avatar() and turned
POST/DELETE /api/agents/{id}/avatar into an unhandled 500 on OpenSandbox-backed
agents. Map it to the WORKSPACE_OP_UNSUPPORTED code the workspace router already
uses for the same condition.
lihongyuan99
force-pushed
the
fix/avatar-delete-unsupported
branch
from
October 5, 2026 23:14
c09c897 to
03d262b
Compare
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
delete_workspace_avatar()treatsOSError/FileNotFoundErroras "nothing to remove", but theharness has a third, documented outcome for a backend that has no delete primitive:
BackendOperationNotSupportedError(octop_harness/backends/workspace.py:973-974). It subclassesRuntimeError, notOSError, so it escapes the loop and reaches the router:POST /api/agents/{agent_id}/avatar—write_workspace_avatar()deletes the other extensionsbefore uploading (
src/octop/infra/agents/experts/avatar.py:97), so an upload can never land.DELETE /api/agents/{agent_id}/avatar— same uncaught exception.Both answer 500 with a bare traceback instead of a structured
OctopError.It is reachable on a backend Octop ships
OpenSandboxis selectable as{"type": "opensandbox"}and Octop installs its dependency ondemand (
src/octop/infra/agents/manager.py:2667-2714). Capability probe against theoctop-harnesspinned in this repo's venv:For
OpenSandbox,sandbox_fs=Truemakes_has_backend_mount()returnFalse(
workspace.py:315-316) andvirtual_mode=Truekeeps_mutation_storage_key()non-None, sodelete()reaches the raise. With the real classes (the optionalopensandboxpackage is notneeded for the delete path), on
develop4667bd7b:DockerSandboxand the cloud backends are unaffected because they implementdelete_path— whichis why this stayed invisible.
Fix
Map the condition to the code the repository already reserves for it, instead of inventing a new
one:
OctopError(ErrorCode.WORKSPACE_OP_UNSUPPORTED, str(exc))→ HTTP 400(
api/routers/workspace.py:56-58_map_workspace_fs_errordoes exactly this for the workspaceroutes;
infra/errors.py:188carries the status). No backend-type branching, perAGENTS.md§5 — the harness error is the signal.Not suppressing it like
mkdirone line above (avatar.py:100) is deliberate: the delete loopenforces "one avatar file per agent", and
read_workspace_avatar()scans.octop/avatar.pngfirst. Skipping the failure would let
aupload_bytes()writeavatar.jpgnext to a staleavatar.pngand return 201 while every client keeps reading the old image. A 400 keeps thecontract honest and writes nothing.
Target branch
develop(feature / fix — default)main(release/*orhotfix/*only)Type of change
Test plan
New unit cases (
tests/unit/agents/test_agent_avatar.py) reuse the file's existing_MemoryWorkspace, so they need no optional dependency and no network:test_delete_maps_unsupported_primitive_to_client_errorandtest_write_refuses_when_avatar_cannot_be_replaced(the latter also pins that a refused uploadleaves no half-written file).
Red before the fix, green after:
make allpasses locally — not run verbatim:make allstarts withformat-all, whichruns Prettier over
dashboard/, and needsmake install-hooks; this clone has nodashboard/node_modulesand the PR touches no frontend file. The three backend gates it coverswere run by hand on this working tree instead:
Full suite (
-n 6for the local run, no LLM calls):3847is the count ondevelop4667bd7bfrom the same venv; the two extra passes are the newcases above.
Checklist
CHANGELOG.md(if user-facing)developRebased onto
develop@ded7adddon 2026-09-30.1.0.2b4and1.0.2b5both shipped since the previous base, and each release commit folded the whole## [Unreleased]list into a version block, which emptied that section ondevelop— it is this branch's only conflict. The resolution keepsdevelop'sCHANGELOG.mdverbatim and re-inserts this PR's single bullet under a fresh### 修复; nothing else changed.Everything below was re-measured on
develop@ded7addd. Backend gates were run by hand, as the equivalents of the CI steps (make lint/make typecheck/make test);make allwas not used because this clone has nodashboard/node_modules.uv run pytest -q tests/unit/agents/test_agent_avatar.py->2 failed, 7 passed in 4.10s9 passed in 1.51suv run ruff check src tests->All checks passed!;uv run ruff format --check src tests->1111 files already formatteduv run mypy src/octop->Success: no issues found in 543 source filesuv run pytest -q -m "not live" -n 6-> 3955 passed, 17 skipped, 1 failed in 214.63s, against 3953 passed, 17 skipped, 1 failed in 225.23s measured on the unmodified base commit in the same virtualenv. The single failure in both runs istests/unit/test_plugin_manager.py::test_install_plugin_importing_legacy_harness_agent:legacy_imports.ensure_legacy_harness_agent_alias()deliberately leaves a realharness_agentdistribution alone, and this local venv has an out-of-lockorcakit-harness-agentinstalled, so the alias assertion cannot hold here. It is pre-existing on the base, not a case this PR adds, and it is green in CI, which installs strictly fromuv.lock(thePython 3.12quality job issuccesson both this branch's pre- and post-rebase heads).tests/livewas not run; no model API key, PostgreSQL or Docker was needed.Rebased onto
develop@83b2ae2e7(2026-10-03).Four commits landed on
developsince this branch's previous base (a02e3e18e): LDAP directory login (#1123), propagating exhausted model retries as failures (#1012), and two dashboard fixes (#1429, #1241) — each appended a bullet to## [Unreleased], soCHANGELOG.mdis this PR's only conflict. The resolution keepsdevelop'sCHANGELOG.mdverbatim and re-inserts this PR's single line into the existing### 修复list:git diff origin/develop 15cfc8d4b -- CHANGELOG.mdis+1/-0, andgit diff 905fce5687 15cfc8d4b2over every non-CHANGELOG file of the PR is empty, i.e. the code you already reviewed is byte-identical.Re-measured on the new base with the same commands and the same virtualenv as the Test plan above:
ruff check src tests→All checks passed!ruff format --check src tests→1133 files already formattedmypy src/octop→Success: no issues found in 551 source filespytest -q tests/unit/agents/test_agent_avatar.py→9 passed in 1.75spytest -q -m "not live" -n 3→ 1 failed, 4134 passed, 17 skipped, 13 warnings in 425.21s (0:07:05) (three branches measured concurrently, so the wall time is inflated), against 4132 passed, 17 skipped, 1 failed in 218.55s measured on the unmodified base83b2ae2e7in this same virtualenv: +2 cases against the base, which are exactly this PR's. The single failure in both runs is the pre-existing local artifacttests/unit/test_plugin_manager.py::test_install_plugin_importing_legacy_harness_agent— this venv has an out-of-lockorcakit-harness-agentdistribution installed, and that test deliberately leaves a realharness_agentpackage alone, so its alias assertion cannot hold here. It is red on the untouched base too and green in CI, which installs strictly fromuv.lock.2026-10-06 —
CHANGELOG.mdre-aligned withdevelop. A rebase of this branch had re-introduced other PRs' release-note bullets into the released## [1.0.2b6]block, so this PR's diff carried those foreign lines instead of only its own.CHANGELOG.mdis nowdevelop's file verbatim plus this PR's single bullet under## [Unreleased]→### 修复:git diff --numstat 75c274aef 03d262bb7 -- CHANGELOG.md→1\t0\tCHANGELOG.mdgit diff --name-only c09c8974c 03d262bb7→CHANGELOG.mdonly, so every code and test file is byte-identical to the head thatPython 3.12andWindows / Python 3.12both reported success on;the commands and counts under Test plan therefore still describe this diff.
CHANGELOG.mdat75c274aefis identical to today'sdeveloptip, so this does not need a fresh rebase.Merged
develop@97d6e7ff4instead of rebasing (2026-10-12); head03d262bb7->7cf35467d.This thread has a human review comment, so it is not eligible for a force-push: the branch was
moved forward with an ordinary
git merge origin/developand a plain (fast-forward) push, no--force/--force-with-leaseinvolved.CHANGELOG.mdwas the only conflict, resolved the sameway as the rebase lanes —
develop's file verbatim plus this PR's own bullet under### 修复,+1/-0against the new base; the code patch is byte-identical to the pre-merge one(
avatar.py+4,test_agent_avatar.py+28, hunk headers aside). Backend gates re-run on themerged head (
make allstill not applicable — nodashboard/node_modules):10 passed in 42.95s4629 passed, 26 skipped, 17 warnings in 439.76s, againstdevelop@97d6e7ff4measured in the same venv: 4627 passed, 26 skipped, 0 failed (the +2 are this PR's two new cases)ruff check src tests-> All checks passed!;ruff format --check src tests-> 1179 files already formatted;mypy src/octop-> Success: no issues found in 570 source filesFolded
developin with a merge commit (this PR has a human comment, so no history rewrite was used)develop@87a8d52c2(2026-10-14);branch tip7cf35467d->47fa90fc8.CHANGELOG.mdis again the only conflict.develop's file is kept verbatim and this PR's ownbullet is re-inserted into the current
## [Unreleased]list, which is now flat (no###subsections), so the diff against the new base is
1 added / 0 deletedthere and the2 code/test file(s) are byte-identical before and after (hunk headers aside) — checked
mechanically, not by eye. Gates re-run by hand on the new head (
make allstill does not apply:no
dashboard/node_modulesin this clone; the venv belongs to this worktree, so/Users/macos/Documents/Qoder/octop-pr-mining/repos/wt-r-1297/src/octop/__init__.pyis this branch's own code):RC ruff=0 format=0 mypy=0 targeted=0 full=010 passed in 2.65s4681 passed, 26 skipped, 41 warnings in 237.43s (0:03:57), againstdevelop@87a8d52c2measured in the same environment today: 4679 passed, 26 skipped, 0 failedAll checks passed!;1185 files already formatted;Success: no issues found in 571 source filesMerged
develop@99e479a6b(2026-10-16) into this branch; heada930a67ed->543a181e3.This branch keeps its history: a reviewer asked for a CHANGELOG re-join on 2026-09-30 and there is prior discussion on it, so the branch is fast-forwarded rather than rewritten.
CHANGELOG.mdis the only thing that had to be re-joined — this PR's bullet is moved back into the current## [Unreleased]→### 修复list,develop's lines are kept verbatim, and the2code/test file(s) are byte-identical (checked on the patch hash, not by eye). Gates re-run by hand on the new head (make allstill does not apply — nodashboard/node_modulesin this clone; the venv belongs to this worktree, so the checkout under test is this branch's own code):RC ruff=0 format=0 mypy=0 targeted=0 full=010 passed in 8.53s4782 passed, 32 skipped, 41 warnings in 411.54s (0:06:51), againstdevelop@99e479a6bmeasured today in its own per-worktree venv on the same machine: 4780 passed, 32 skipped, 0 failed in 495.68sAll checks passed!;1203 files already formatted;Success: no issues found in 581 source files