Skip to content

fix(harness): isolate sandbox binding per call to fix concurrent corruption - #2675

Open
larry-zy wants to merge 1 commit into
agentscope-ai:mainfrom
larry-zy:fix/harness-2490-concurrent-sandbox-corruption
Open

fix(harness): isolate sandbox binding per call to fix concurrent corruption#2675
larry-zy wants to merge 1 commit into
agentscope-ai:mainfrom
larry-zy:fix/harness-2490-concurrent-sandbox-corruption

Conversation

@larry-zy

@larry-zy larry-zy commented Aug 12, 2026

Copy link
Copy Markdown

Related: #2490

Summary

SandboxLifecycleMiddleware and SandboxBackedFilesystem held the acquired sandbox in a single agent-level slot (an AtomicReference plus the proxy's volatile field). Since
AgentBase.serializeOnKey only serialises calls with the same (userId, sessionId), calls from distinct sessions run in parallel on one agent bean and raced on that shared slot,
causing two failures:

  • A call could execute against another session's sandbox.
  • A finishing call's release could tear down a still-live sibling sandbox.

Changes

  • Bind the acquired SandboxAcquireResult per call on the invocation's RuntimeContext, and resolve it there first, so concurrent distinct-session calls stay isolated.
  • releaseForCall now reads back its own binding and tears down only its own sandbox.
  • Keep the volatile field as a best-effort fallback for context-free internal callers that resolve the filesystem with a shared empty RuntimeContext (e.g.
    WorkspaceMessageBus). To stop that fallback from being clobbered under concurrency, it's now maintained via a synchronized compare-and-clear (clearSandboxIfCurrent), so a
    releasing call never nulls a sibling's binding.

Testing

Adds SandboxLifecycleConcurrencyReproTest, which drives the legal A.acquire → B.acquire → A.use → A.release interleaving and asserts each call executes against its own sandbox
and that A's release affects only A.
mvn -pl agentscope-harness test
-Dtest='SandboxLifecycleConcurrencyReproTest,SandboxBackedFilesystemTest,SandboxLifecycleMiddlewareCallbackTest'

All related tests pass (18 run, 0 failures, 0 errors).

…uption (agentscope-ai#2490)

SandboxLifecycleMiddleware and SandboxBackedFilesystem held the acquired
sandbox in a single agent-level slot (an AtomicReference plus the proxy's
volatile field). Because AgentBase.serializeOnKey only serialises same
(userId, sessionId) calls, distinct-session calls run in parallel on one
agent bean and raced on that slot: one call would execute against another
session's sandbox, and a finishing call's release would tear down a
still-live sibling sandbox.

Bind the acquired SandboxAcquireResult per call on the invocation's
RuntimeContext and resolve it there first, so concurrent distinct-session
calls stay isolated. releaseForCall reads back its own binding and tears
down only its own sandbox.

The volatile field is retained as a best-effort fallback for context-free
internal callers that resolve the filesystem with a shared empty
RuntimeContext (e.g. WorkspaceMessageBus). To keep that fallback from
being clobbered across concurrent calls, the field is now maintained via a
synchronized compare-and-clear (clearSandboxIfCurrent) so a releasing call
never nulls a sibling's binding.

Adds SandboxLifecycleConcurrencyReproTest, which drives the legal
A.acquire -> B.acquire -> A.use -> A.release interleaving and asserts each
call executes against its own sandbox and A's release affects only A.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.27273% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...s/agent/middleware/SandboxLifecycleMiddleware.java 55.55% 3 Missing and 1 partial ⚠️
...nt/filesystem/sandbox/SandboxBackedFilesystem.java 92.30% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@AgentScopeJavaBot AgentScopeJavaBot added bug Something isn't working area/harness agentscope-harness (test/runtime support) labels Aug 13, 2026

@AgentScopeJavaBot AgentScopeJavaBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review

This PR fixes issue #2490 — concurrent calls for distinct (userId, sessionId) sessions on the same agent bean corrupted each other's sandbox binding. The root cause was a single agent-level slot (volatile Sandbox field + currentAcquireResult) shared across all concurrent calls. The fix moves the sandbox binding to the per-call RuntimeContext (typed attribute), with the volatile field retained as a best-effort fallback for context-free callers (e.g., WorkspaceMessageBus). A compareAndClear pattern (synchronized + identity check) prevents one call's release from nulling another's fallback. A deterministic concurrency test reproduces the exact interleaving and validates three isolation invariants. The fix is architecturally sound and correctly addresses the reported issue.

@AgentScopeJavaBot AgentScopeJavaBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review

This PR fixes issue #2490 — concurrent calls for distinct (userId, sessionId) sessions on the same agent bean corrupted each other's sandbox binding. The root cause was a single agent-level slot (volatile Sandbox field + currentAcquireResult) shared across all concurrent calls. The fix moves the sandbox binding to the per-call RuntimeContext (typed attribute), with the volatile field retained as a best-effort fallback for context-free callers (e.g., WorkspaceMessageBus). A compareAndClear pattern (synchronized + identity check) prevents one call's release from nulling another's fallback. A deterministic concurrency test reproduces the exact interleaving and validates three isolation invariants. The fix is architecturally sound and correctly addresses the reported issue.

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

Labels

area/harness agentscope-harness (test/runtime support) bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants