{"record":{"id":"97358443e9d45a3b","repo":"paperclipai/paperclip","slug":"fake-setup-session-cannot-be-captured-from-status","errorCode":null,"errorMessage":"Fake setup session cannot be captured from status ${state.status}.","messagePattern":"Fake setup session cannot be captured from status (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/paperclip-plugin-fake-sandbox/src/plugin.ts","lineNumber":431,"sourceCode":"        metadata: {\n          provider: \"fake-plugin\",\n          found: false,\n        },\n      };\n    }\n    return presentSetupSession(state, { includeConnectionPayload: params.includeConnectionPayload === true });\n  },\n\n  async onEnvironmentCaptureTemplate(\n    params: PluginEnvironmentCaptureTemplateParams,\n  ): Promise<PluginEnvironmentCaptureTemplateResult> {\n    const config = parseConfig(params.config);\n    const state = params.providerLeaseId ? setupSessions.get(params.providerLeaseId) : undefined;\n    if (!state) {\n      throw new Error(\"Fake setup session not found.\");\n    }\n    if (state.status === \"cancelled\" || state.status === \"timed_out\" || state.status === \"failed\") {\n      throw new Error(`Fake setup session cannot be captured from status ${state.status}.`);\n    }\n\n    state.status = \"capturing\";\n    const templateRef = buildTemplateRef(state.environmentId, state.sessionId);\n    const template: FakeTemplateState = {\n      templateRef,\n      environmentId: state.environmentId,\n      sessionId: state.sessionId,\n      image: config.image,\n      sourceTemplateRef: params.sourceTemplateRef ?? state.sourceTemplateRef,\n      previousTemplateRef: params.previousTemplateRef ?? null,\n      deleted: false,\n    };\n    templates.set(templateRef, template);\n    state.status = \"promoted\";\n    state.capturedTemplateRef = templateRef;\n    await removeLease(state.providerLeaseId);\n","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/paperclip-plugin-fake-sandbox/src/plugin.ts#L413-L449","documentation":"Thrown by onEnvironmentCaptureTemplate() after the setup session was located, but its status is one of cancelled, timed_out, or failed. Capturing a template requires a session in an active state (e.g. waiting_for_user, capturing); a terminal-failure session cannot be promoted into a snapshot. The offending status is interpolated into the message.","triggerScenarios":"Calling captureTemplate after onEnvironmentCancelInteractiveSetup() already moved the session to cancelled. Calling capture after the session timed out (reason \"timed_out\") or failed. A test sequenced cancel-before-capture.","commonSituations":"Test ordering bug where cancel precedes capture. Real-world flow where a setup timed out and the caller still tried to snapshot it. Reusing a session id after an explicit cancellation.","solutions":["Start a fresh interactive setup session via onEnvironmentStartInteractiveSetup() and capture from that new lease.","Inspect the session status via onEnvironmentGetInteractiveSetup() before attempting capture; abort if status is terminal.","Fix the test/caller ordering so capture is attempted only on a live session."],"exampleFix":"// before\nawait plugin.onEnvironmentCancelInteractiveSetup({ providerLeaseId, reason: \"user_cancelled\" });\nawait plugin.onEnvironmentCaptureTemplate({ providerLeaseId, ... });\n// after\nconst session = await plugin.onEnvironmentStartInteractiveSetup({ environmentId, sessionId, ... });\nawait plugin.onEnvironmentCaptureTemplate({ providerLeaseId: session.providerLeaseId, ... });","handlingStrategy":"validation","validationCode":"const TERMINAL = new Set([\"cancelled\", \"timed_out\", \"failed\"]);\nconst session = await plugin.onEnvironmentGetInteractiveSetup({ providerLeaseId });\nif (!session || session.status === \"missing\" || TERMINAL.has(session.status)) {\n  // start a new session instead of capturing\n}","typeGuard":"function isCapturableStatus(status: string): boolean {\n  return ![\"cancelled\", \"timed_out\", \"failed\", \"missing\"].includes(status);\n}","tryCatchPattern":"try {\n  await plugin.onEnvironmentCaptureTemplate({ providerLeaseId, ... });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Fake setup session cannot be captured\")) {\n    // open a fresh session and retry\n  } else throw err;\n}","preventionTips":["Order operations: capture only on a live session.","Inspect status before capture.","Avoid cancelling then capturing in tests."],"tags":["fake-sandbox","state-machine","environment","test-double"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}