Sandcastle resumeSession Constraints: Validation Rules and File Requirements

The resumeSession feature in Sandcastle requires maxIterations to be set to 1 and mandates that the session JSONL file exists on the host filesystem at ~/.claude/projects/<encoded>/<id>.jsonl before the agent can resume the Claude Code session.

The resumeSession feature in mattpocock/sandcastle allows you to continue a previously saved Claude Code agent run inside a sandboxed environment. Before invoking this capability, the framework enforces specific constraints to ensure session integrity and prevent iteration conflicts.

Validation Constraints for resumeSession

The run function in src/run.ts enforces two hard constraints before allowing a session to resume.

maxIterations Must Equal 1

The resume logic only applies to the first iteration of the agent loop. In src/run.ts lines 52-58, the code validates that maxIterations is explicitly set to 1. This constraint exists because the session restoration hook executes only at the beginning of the first iteration, and multi-iteration runs could lead to undefined behavior when combined with resumed state.

Session File Must Exist on Host

The framework validates that the referenced session file exists on the host filesystem before attempting to transfer it to the sandbox. In src/run.ts lines 76-84, the code checks for the presence of the JSONL file at ~/.claude/projects/<encoded>/${resumeSession}.jsonl. If the file is missing, the function throws an Error with a clear message before any sandbox operations begin.

How Session Transfer Works

Once validation passes, Sandcastle coordinates the movement of session data between the host and the sandbox environment.

Host and Sandbox Session Stores

In src/SessionStore.ts, the transferSession(from, to, id) function (lines 20-70 and 119-136) handles the actual file movement. The implementation defines two concrete stores:

  • hostSessionStore: Reads and writes JSONL files in ~/.claude/projects/<encoded>/ on the host machine
  • sandboxSessionStore: Copies files through the bind-mount sandbox handle or via docker cp/podman cp for isolated environments

Orchestrator Execution

In src/Orchestrator.ts lines 79-99, the per-iteration loop constructs both stores and invokes transferSession when i === 1 and a resumeSession ID is present. This copies the JSONL file into the sandbox's ~/.claude/projects/<encoded>/ directory before the agent starts.

CLI Flag Injection

After transferring the session file, Sandcastle must instruct Claude Code to load it. In src/AgentProvider.ts lines 70-74, the buildPrintCommand method appends the --resume <id> flag to the CLI command. This instructs the agent to restore its internal conversation state, tool call history, and token usage from the supplied JSONL file.

Error Handling

The feature implements specific error handling for constraint violations:

  • Missing file: If the session file does not exist during validation, src/run.ts throws a standard Error (lines 77-84)
  • Transfer failure: If the file copy fails due to permissions or network issues, the Orchestrator wraps the error in a SessionCaptureError (lines 92-98 in src/Orchestrator.ts)

Usage Example

import { run } from "./src/run.js";
import { claudeCode } from "./src/AgentProvider.js";
import { docker } from "./src/SandboxFactory.js";

await run({
  agent: claudeCode("claude-opus-4-7"),
  sandbox: docker({ imageName: "sandcastle:myrepo" }),
  cwd: "/my/project",
  prompt: "Continue the previous analysis from where we left off",
  resumeSession: "abc-123", // File must exist at ~/.claude/projects/<encoded>/abc-123.jsonl
  maxIterations: 1,          // Required constraint
});

Running this script validates the constraints, transfers the session file into the sandbox, and launches claude --resume abc-123 to continue the previous conversation exactly where it paused.

Summary

  • Set maxIterations: 1 when using resumeSession to satisfy the validation logic in src/run.ts
  • Verify file existence at ~/.claude/projects/<encoded>/<id>.jsonl before running, or the framework throws an error
  • Transfer mechanism uses SessionStore.transferSession called by the Orchestrator at the first iteration only
  • CLI integration occurs via AgentProvider.ts which appends the --resume <id> flag to the Claude Code command
  • Error types include standard Error for missing files and SessionCaptureError for failed transfers

Frequently Asked Questions

What happens if I set maxIterations greater than 1 with resumeSession?

The validation logic in src/run.ts lines 52-58 will throw an error preventing execution. The resume hook is designed to run only once at the start of the first iteration, making multi-iteration resumes architecturally incompatible with the current implementation.

Where does Sandcastle look for the session file?

According to the source code in src/run.ts lines 76-84, Sandcastle expects the file at ~/.claude/projects/<encoded>/${resumeSession}.jsonl on the host filesystem, where <encoded> represents the encoded project path. The file must exist before the run function executes.

Can I resume a session in an isolated sandbox without bind mounts?

Yes. While bind-mount providers allow direct file access, isolated sandboxes use the sandboxSessionStore implementation which transfers files via docker cp or podman cp mechanisms. The transferSession abstraction in src/SessionStore.ts handles both transparently.

What error occurs if the session file is missing?

If the JSONL file does not exist at the expected path, src/run.ts throws an Error with a descriptive message explaining that the session file could not be found. This validation occurs before any sandbox container operations, preventing wasted resources on failed runs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →