ruvnet/ruflo · error

Events and contexts arrays must have same length

Error message

Events and contexts arrays must have same length

What it means

ParallelHookExecutor.executeParallel(events, contexts, options) zips two arrays by index: events[i] is executed with contexts[i], and batches of size maxParallel are run with Promise.allSettled. It throws synchronously before any hook runs when the two arrays have different lengths, because there is no defensible pairing. This is a caller precondition violation, not a hook execution failure.

Source

Thrown at v3/@claude-flow/shared/src/hooks/executor.ts:247

      };
    }
  }

  /**
   * Execute multiple hooks in parallel
   *
   * @param events - Array of hook events
   * @param contexts - Array of contexts (matched by index)
   * @param options - Execution options
   * @returns Array of aggregated results
   */
  async executeParallel(
    events: HookEvent[],
    contexts: HookContext[],
    options: HookExecutionOptions = {}
  ): Promise<AggregatedHookResult[]> {
    if (events.length !== contexts.length) {
      throw new Error('Events and contexts arrays must have same length');
    }

    const maxParallel = options.maxParallel || events.length;
    const results: AggregatedHookResult[] = [];

    // Execute in batches
    for (let i = 0; i < events.length; i += maxParallel) {
      const batch = events.slice(i, i + maxParallel);
      const batchContexts = contexts.slice(i, i + maxParallel);

      const batchResults = await Promise.allSettled(
        batch.map((event, index) =>
          this.execute(event, batchContexts[index], options)
        )
      );

      for (const result of batchResults) {
        if (result.status === 'fulfilled') {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Derive contexts from events so lengths are structurally equal: const contexts = events.map(e => contextFor(e))
  2. If one shared context applies to every event, pass events.map(() => sharedContext) instead of [sharedContext]
  3. Add a dev-time assertion events.length === contexts.length at the construction site so the bug is caught where the arrays are built, not inside the executor
  4. If the two lists legitimately differ, loop over execute(event, context, options) instead of executeParallel()

Example fix

// before
const events = enabledHooks.map(h => h.event);   // 3 entries
const contexts = [context];                        // 1 entry
await executor.executeParallel(events, contexts);   // throws

// after
const events = enabledHooks.map(h => h.event);
const contexts = events.map(() => context);         // matched by index
await executor.executeParallel(events, contexts);
Defensive patterns

Strategy: validation

Validate before calling

if (events.length !== contexts.length) {
  throw new RangeError(
    `executeParallel precondition failed: ${events.length} events vs ${contexts.length} contexts`
  );
}
await executor.executeParallel(events, contexts);

Type guard

function isIndexMatched<T, U>(events: T[], contexts: U[]): boolean {
  return Array.isArray(events) && Array.isArray(contexts) && events.length === contexts.length;
}

Try / catch

try {
  await executor.executeParallel(events, contexts);
} catch (e) {
  if (e instanceof Error && e.message.includes('same length')) {
    // caller-side data bug: rebuild the arrays, do NOT retry with the same inputs
    throw new Error(`hook batch malformed: ${events.length} events / ${contexts.length} contexts`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling executeParallel(['pre-task','post-task'], [ctx]) after appending an event without its context; building events with a filter (e.g. only enabled hooks) while passing an unfiltered contexts array; reusing a cached contexts array after the events list changed; passing events with contexts.length === 0.

Common situations: Refactoring from execute() (single event + single context) to executeParallel() and forgetting the contexts array must grow too; dynamic event lists composed at runtime paired with a static one-element context list; copy-pasting a call site and editing one array but not the other.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/2ef36ccfdb642131. Report an issue: GitHub.