can1357/oh-my-pi · error · StructuredSubagentError
Subagent execution failed: ${error instanceof Error ? error.
Error message
Subagent execution failed: ${error instanceof Error ? error.message : String(error)} What it means
Generic execution-stage wrapper: any error thrown while the subagent actually runs (after preflight/isolation succeed) is wrapped in a StructuredSubagentError at stage "execution" with the original error as `cause`. Existing StructuredSubagentErrors pass through unwrapped, so this only surfaces unexpected failures from the agent run itself.
Source
Thrown at packages/coding-agent/src/task/structured-subagent.ts:664
else if (result.patchPath)
mergeSummary = `\n\nIsolation: changes captured at \`${result.patchPath}\` (apply=false). Not applied.`;
else if ((result.nestedPatches?.length ?? 0) > 0)
mergeSummary = `\n\nIsolation: changes captured for ${result.nestedPatches?.length} nested ${(result.nestedPatches?.length ?? 0) === 1 ? "repository" : "repositories"} (apply=false). Not applied.`;
else mergeSummary = "\n\nIsolation: no changes captured.";
}
completedSuccessfully = result.exitCode === 0 && !result.error && !result.aborted;
return {
result,
policy,
mergeSummary,
changesApplied,
artifactsDir: lease.artifactsDir,
temporaryArtifacts: lease.temporary,
};
} catch (error) {
if (error instanceof StructuredSubagentError) throw error;
throw new StructuredSubagentError(
"execution",
`Subagent execution failed: ${error instanceof Error ? error.message : String(error)}`,
{ cause: error },
);
} finally {
const shouldRetainArtifacts =
(request.retainArtifacts && completedSuccessfully) ||
(policy.isIsolated && (!policy.applyChanges || changesApplied === false || requiresRecoveryArtifacts));
const shouldCleanup = lease.temporary && !shouldRetainArtifacts;
if (shouldCleanup) {
const cleanupArtifacts = async (): Promise<void> => {
await fs.rm(lease.artifactsDir, { recursive: true, force: true });
lease.unregister?.();
};
if (deferredCleanup) {
trackLateCleanup(deferredCleanup.then(cleanupArtifacts), {
resource: "artifacts",
artifactsDir: lease.artifactsDir,View on GitHub (pinned to 9690622007)
Solutions
- Inspect `error.cause` (or the message suffix) for the root failure and fix that
- Retry the task if the cause was transient (network/provider error)
- Add error handling in the subagent's tools/prompts so failures surface as controlled errors
Example fix
// caller handling
try {
const result = await runStructuredSubagent(req);
} catch (e) {
if (e instanceof StructuredSubagentError && e.stage === "execution") {
logger.error("subagent failed", { cause: e.cause }); // inspect and retry if transient
}
} Defensive patterns
Strategy: try-catch
Type guard
function isExecutionStageError(e: unknown): e is StructuredSubagentError {
return e instanceof StructuredSubagentError && e.stage === "execution";
} Try / catch
try {
return await runStructuredSubagent(req);
} catch (e) {
if (isExecutionStageError(e)) {
logger.error("subagent execution failed", { cause: e.cause });
if (isTransient(e.cause)) return withRetry(() => runStructuredSubagent(req));
}
throw e;
} Prevention
- Always inspect error.cause to find the real failure
- Add retry/backoff for transient provider/network causes
- Handle errors inside subagent tools so failures surface as controlled StructuredSubagentErrors instead
When it happens
Trigger: The spawned agent run throws — model/API errors, tool crashes, unhandled exceptions inside the subagent loop, aborted sessions — anything not already a StructuredSubagentError.
Common situations: LLM provider outages or auth failures during the run; a tool inside the subagent crashing; bugs in agent prompts/handlers causing unhandled rejections; network interruptions mid-run.
Related errors
- ${message}${hint}
- [vibe:${record.id} cli=${record.cli} turn=${turnIndex}] turn
- transparent (brush_core::Error)
- lines.join("\n")
- agent() blocked: turn token budget exhausted (${turnBudget.s
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a0d98856f4b77a6c.
Report an issue: GitHub.