paperclipai/paperclip · error · Error
Daytona template capture requires @daytonaio/sdk Sandbox._ex
Error message
Daytona template capture requires @daytonaio/sdk Sandbox._experimental_createSnapshot support.
What it means
Thrown by the Daytona plugin's onEnvironmentCaptureTemplate hook after it resolves the setup sandbox. The plugin reads the optional Sandbox._experimental_createSnapshot method (typed optional on DaytonaInteractiveSandbox) and refuses to capture a template when the resolved sandbox instance does not expose it, rather than calling undefined. Because the method is namespaced _experimental_*, its presence depends on the installed @daytonaio/sdk version (currently pinned to 0.203.0) and on the sandbox having been produced through the interactive-setup code path.
Source
Thrown at packages/plugins/sandbox-providers/daytona/src/plugin.ts:2255
async onEnvironmentCaptureTemplate(
params: PluginEnvironmentCaptureTemplateParams,
): Promise<PluginEnvironmentCaptureTemplateResult> {
const config = parseDriverConfig(params.config);
if (!params.providerLeaseId) {
throw new Error("Cannot capture a Daytona template without a setup sandbox lease.");
}
const scope = {
driverKey: params.driverKey,
companyId: params.companyId,
environmentId: params.environmentId,
providerLeaseId: params.providerLeaseId,
config,
};
return await withSandboxActivityGate(scope, async () => {
const sandbox = await getSandbox(scope, { bypassTeardownGate: true });
const createSnapshot = (sandbox as DaytonaInteractiveSandbox)._experimental_createSnapshot;
if (typeof createSnapshot !== "function") {
throw new Error(
"Daytona template capture requires @daytonaio/sdk Sandbox._experimental_createSnapshot support.",
);
}
const templateRef = sanitizeSnapshotName(
params.templateLabel,
`paperclip-${params.environmentId}-${randomUUID().slice(0, 8)}`,
);
const timeoutMs = typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0
? Math.trunc(params.timeoutMs)
: config.timeoutMs;
await createSnapshot.call(sandbox, templateRef, toTimeoutSeconds(timeoutMs));
return {
templateKind: "snapshot",
templateRef,
metadata: {
provider: "daytona",View on GitHub (pinned to 67001ec6eb)
Solutions
- Pin or restore @daytonaio/sdk to a version that exposes Sandbox._experimental_createSnapshot (the repo currently pins 0.203.0); run pnpm install and rebuild the daytona provider.
- Verify the capture is being called against a lease returned by startInteractiveSetup so the handle is a DaytonaInteractiveSandbox, not a bare client.get result.
- If upgrading the SDK, re-check the DaytonaInteractiveSandbox type at plugin.ts:142 and update the method name / signature to match the new SDK before re-enabling capture.
- Temporarily disable template capture for this provider (do not advertise environmentCaptureTemplate) until the SDK stabilizes the snapshot API.
Example fix
// before (package.json) "@daytonaio/sdk": "0.210.0" // after "@daytonaio/sdk": "0.203.0"
Defensive patterns
Strategy: type-guard
Validate before calling
import type { Sandbox } from '@daytonaio/sdk';
type WithSnapshot = Sandbox & {
_experimental_createSnapshot?: (n: string, t?: number) => Promise<void>;
};
async function safeCapture(
sandbox: WithSnapshot,
name: string,
timeoutSeconds: number,
): Promise<boolean> {
if (typeof sandbox._experimental_createSnapshot !== 'function') {
return false; // caller skips onEnvironmentCaptureTemplate
}
await sandbox._experimental_createSnapshot(name, timeoutSeconds);
return true;
} Type guard
function supportsCreateSnapshot(
s: Sandbox & { _experimental_createSnapshot?: unknown },
): s is Sandbox & {
_experimental_createSnapshot: (name: string, timeout?: number) => Promise<void>;
} {
return typeof (s as { _experimental_createSnapshot?: unknown })._experimental_createSnapshot === 'function';
} Try / catch
try {
await plugin.onEnvironmentCaptureTemplate(params);
} catch (err) {
if (err instanceof Error && /_experimental_createSnapshot/.test(err.message)) {
// SDK lacks the method: skip capture, do not crash the run
return { templateKind: null, templateRef: null };
}
throw err;
} Prevention
- Pin @daytonaio/sdk (the repo pins 0.203.0) and lockfile-verify before deploy.
- Advertise environmentCaptureTemplate only when the loaded SDK exposes the method; gate the capability on a startup probe.
- In tests, always stub _experimental_createSnapshot on the mock sandbox.
When it happens
Trigger: Calling onEnvironmentCaptureTemplate (template capture from a finished interactive setup) when (a) @daytonaio/sdk has been bumped to a version that renamed or removed _experimental_createSnapshot, (b) the cached handle was repopulated by a plain client.get and its prototype lacks the experimental method, or (c) a test/mock sandbox object does not stub _experimental_createSnapshot.
Common situations: SDK upgrade/downgrade churn while the method is still experimental; CI or unit runs using a stub Sandbox that only implements stable methods; attempting to capture a template from a lease that was not created via startInteractiveSetup.
Related errors
- Daytona template deletion requires @daytonaio/sdk snapshot.g
- Daytona sync ${label} path is not a confined absolute path:
- Daytona sync ${label} path escapes the workspace remote dir:
- Daytona syncOut refusing tarball with an unparseable entry l
- Daytona syncOut refusing unparseable symlink entry: ${line}
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/fcc694c4b7d56e06.
Report an issue: GitHub.