{"record":{"id":"a4ebb35c7ad4c79c","repo":"vercel/ai","slug":"preparesandboxforharness-at-least-one-harness-mus","errorCode":null,"errorMessage":"prepareSandboxForHarness: at least one harness must be provided.","messagePattern":"prepareSandboxForHarness: at least one harness must be provided\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness/src/agent/prepare-sandbox-for-harness.ts","lineNumber":53,"sourceCode":" * committing, snapshotting, or otherwise persisting that modified filesystem.\n * When a later `HarnessAgent` session uses a sandbox created from the persisted\n * artifact, the adapter recomputes the same recipe identity and the existing\n * bootstrap marker makes the bootstrap logic a no-op.\n *\n * Repeated harness IDs are prepared once. When multiple adapters use the same\n * ID, the last adapter in `harnesses` is used.\n */\nexport async function prepareSandboxForHarness(options: {\n  readonly session: SandboxSession;\n  readonly harnesses: ReadonlyArray<HarnessAgentAdapter>;\n  readonly sandboxConfig?: HarnessAgentSandboxConfig;\n  readonly abortSignal?: AbortSignal;\n}): Promise<PrepareSandboxForHarnessResult> {\n  const sandboxConfig = options.sandboxConfig ?? {};\n  validateSandboxBootstrapSettings(sandboxConfig);\n\n  if (options.harnesses.length === 0) {\n    throw new Error(\n      'prepareSandboxForHarness: at least one harness must be provided.',\n    );\n  }\n\n  const harnesses = [\n    ...new Map(\n      options.harnesses.map(harness => [harness.harnessId, harness]),\n    ).values(),\n  ].sort((a, b) => a.harnessId.localeCompare(b.harnessId));\n\n  const workDir =\n    sandboxConfig.workDir == null\n      ? undefined\n      : normalizeSandboxWorkDir(sandboxConfig.workDir);\n  const recipeIdentities: Record<string, string> = {};\n  const skippedHarnessIds: string[] = [];\n  let defaultWorkingDirectory: string | undefined;\n","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness/src/agent/prepare-sandbox-for-harness.ts#L35-L71","documentation":"prepareSandboxForHarness exists to pre-warm a sandbox for one or more harnesses; calling it with an empty harnesses array would be a no-op with misleading success semantics, so it throws. At least one harness entry is required.","triggerScenarios":"Calling prepareSandboxForHarness({ harnesses: [] }) — e.g. when the harness list is built conditionally and every condition was false.","commonSituations":"Dynamically collecting harnesses from feature flags that are all disabled; filtering the harness array before the call and accidentally emptying it.","solutions":["Pass at least one harness in the harnesses array.","Guard the call: only invoke prepareSandboxForHarness when harnesses.length > 0.","Fix the logic that builds the harnesses list so it isn't empty."],"exampleFix":"// before\nawait prepareSandboxForHarness({ harnesses: [], sandboxConfig });\n// after\nif (harnesses.length > 0) {\n  await prepareSandboxForHarness({ harnesses, sandboxConfig });\n}","handlingStrategy":"validation","validationCode":"if (!harnesses || harnesses.length === 0) return; // or throw early with context","typeGuard":null,"tryCatchPattern":"try { await prepareSandboxForHarness({ harnesses, sandboxConfig }); } catch (e) { if (e.message.includes('at least one harness')) { /* skip pre-warm or build harnesses */ } else throw e; }","preventionTips":["Guard optional pre-warm calls with harnesses.length > 0","Avoid building harness lists from all-optional flags without an emptiness check","Log when the harness list ends up empty to surface config bugs"],"tags":["sandbox","validation","configuration"],"backgroundTag":"empty-required-array","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}