# What Happens When a Parallel Branch Fails During the Diverge Phase in ADHD: Error Handling Explained

> Learn how ADHD handles parallel branch failures during diverge phase. Discover error handling and pipeline continuity for uninterrupted processing.

- Repository: [Udit Akhouri/adhd](https://github.com/UditAkhourii/adhd)
- Tags: internals
- Published: 2026-07-30

---

**When a parallel branch fails during the diverge phase in ADHD, the engine catches the parsing error in `divergeBranch` and returns an empty ideas array for that specific cognitive frame, allowing the pipeline to continue uninterrupted with the remaining successful branches.**

The ADHD engine orchestrates a multi-phase cognitive reasoning pipeline where the **diverge phase** fans out parallel LLM invocations across distinct cognitive frames defined in [`src/frames.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/frames.ts). Understanding how the system handles individual branch failures is critical for building resilient AI workflows, as the implementation in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts) demonstrates a graceful degradation pattern rather than a hard failure model.

## The Diverge Phase and Parallel Branch Execution

During the diverge phase, ADHD instantiates multiple parallel branches—each representing a different cognitive frame stored in [`src/frames.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/frames.ts). The engine invokes the LLM independently for each branch, with no cross-branch visibility, to generate diverse ideas for the input problem. These branches execute concurrently under a `p-limit` concurrency guard to manage API rate limits and resource utilization.

The core branching logic resides in the `divergeBranch` helper function within [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts). Each branch receives a specific frame, constructs a tailored prompt, and awaits the LLM response. This architecture assumes that individual branch failures are inevitable in distributed LLM operations and must not compromise the entire pipeline.

## How the Engine Handles Branch Failures

When a branch encounters an error—typically a JSON parsing failure when the LLM response does not conform to the expected `DivergeRowSchema`—the `divergeBranch` function implements a defensive catch block that isolates the failure:

```typescript
// src/engine.ts – divergeBranch
async function divergeBranch(
  problem: string,
  context: string | undefined,
  frame: Frame,
  ideasPerFrame: number,
  model: string | undefined,
): Promise<Branch> {
  const raw = await callLLM({
    model,
    systemPrompt: DIVERGE_SYSTEM,
    userPrompt: /* constructed prompt */,
  });

  let rows: z.infer<typeof DivergeRowSchema>;
  try {
    rows = parseJSON(raw, DivergeRowSchema);
  } catch {
    // **Failure path** – produce no ideas for this branch
    return { frameId: frame.id, ideas: [] };
  }

  const ideas: Idea[] = rows.map((r) => ({
    id: randomUUID(),
    frameId: frame.id,
    text: r.text,
    rationale: r.rationale,
    depth: 0,
  }));
  return { frameId: frame.id, ideas };
}

```

The failure path explicitly returns a `Branch` object containing the `frameId` but an empty `ideas` array. This design ensures type consistency while signaling that the particular cognitive frame produced no valid output.

### Consequences of a Failed Branch

When `parseJSON` throws an exception and the branch returns empty, the following consequences propagate through the system:

1. **Zero contribution from the failed frame.** The specific cognitive frame contributes no ideas to the collective pool.
2. **Pipeline continuity is preserved.** The `run` method in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts) collects all branches via `Promise.all`, aggregating results regardless of individual branch success:
   ```typescript
   // src/engine.ts – divergence loop
   const branches = await Promise.all(
     frames.map((f) =>
       limit(async () => {
         const b = await divergeBranch(divergeProblem, context, f, ideasPerFrame, model);
         return b;               // May be empty if the branch failed
       })
     )
   );
   ```

3. **Downstream phases operate on available data.** The scoring, clustering, and deepening phases process only the ideas gathered from successful branches, as defined by the `Branch` and `Idea` types in [`src/types.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/types.ts).

## Impact on Downstream Phases

The failure handling in the diverge phase creates a resilient foundation for subsequent operations. Since the `allIdeas` array (constructed by flattening successful branch outputs) simply excludes the empty arrays from failed branches, the scoring and clustering algorithms remain unaware of the missing frame. This approach prioritizes **availability over completeness**, ensuring that partial cognitive coverage is delivered rather than failing the entire request.

The type definitions in [`src/types.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/types.ts) enforce this contract by requiring every branch to return an array of `Idea` objects, even if that array is empty. Consequently, the clustering logic in later phases can safely assume array structures without null checks for individual branch results.

## Summary

- **Graceful degradation:** Failed branches return `{ frameId, ideas: [] }` rather than throwing fatal errors, as implemented in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts).
- **Pipeline continuation:** The `Promise.all` aggregation in the divergence loop proceeds with successful branches even when individual frames fail.
- **No downstream impact:** Scoring and clustering operate only on the subset of ideas successfully generated by valid branches.
- **Source locations:** Core logic resides in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts) (`divergeBranch` function), with type definitions in [`src/types.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/types.ts) and frame configurations in [`src/frames.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/frames.ts).

## Frequently Asked Questions

### Does a failed branch stop the entire ADHD pipeline?

No. According to the [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts) implementation, a failed branch is caught within the `divergeBranch` helper and returns an empty ideas array. The parent `run` function collects results via `Promise.all`, which waits for all branches—including failed ones—to settle before proceeding to the scoring phase.

### What triggers the empty ideas fallback in the diverge phase?

The empty array fallback triggers when `parseJSON(raw, DivergeRowSchema)` throws an exception, typically because the LLM returned malformed JSON, unexpected schema shapes, or content that violates the Zod schema validation. Any parsing or validation error results in the catch block executing `return { frameId: frame.id, ideas: [] }`.

### How does ADHD ensure other branches complete if one fails?

ADHD wraps each branch invocation in a `p-limit` concurrency guard and uses `Promise.all` to await all promises simultaneously. Since each `divergeBranch` call handles its own errors internally and never rejects, `Promise.all` always resolves successfully, allowing the engine to flatten all results—empty or populated—into the final ideas collection.

### Are there retries for failed branches in the current implementation?

The current implementation in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts) does not implement automatic retries within the `divergeBranch` function. Upon catching a parsing error, the function immediately returns an empty array. Users seeking retry logic would need to implement it at the `callLLM` level or wrap the engine invocation in external orchestration.