builderman

A dependency-aware task runner for building, developing, and orchestrating complex workflows

Error Handling Guarantees

builderman pipelines never throw.

All failures — including task errors, invalid configuration, cancellation, and process termination — are reported through a structured RunResult.

Structured Error Handling

import { pipeline, PipelineError } from "builderman"

const result = await pipeline([backend, frontend]).run()

if (!result.ok) {
  switch (result.error.code) {
    case PipelineError.Aborted:
      console.error("Pipeline was cancelled")
      break
    case PipelineError.TaskFailed:
      console.error("Task failed:", result.error.message)
      break
    case PipelineError.TaskReadyTimeout:
      console.error("Task was not ready in time:", result.error.message)
      break
    case PipelineError.TaskCompletedTimeout:
      console.error("Task did not complete in time:", result.error.message)
      break
    case PipelineError.ProcessTerminated:
      console.error("Process terminated:", result.error.message)
      break
    case PipelineError.InvalidTask:
      console.error("Invalid task configuration:", result.error.message)
      break
  }
}

Error Codes

PipelineError.Aborted

The pipeline was cancelled using an AbortSignal.

PipelineError.TaskFailed

A task's command exited with a non-zero exit code or threw an error.

PipelineError.TaskReadyTimeout

A task with a readyWhen predicate did not become ready within the timeout period.

PipelineError.TaskCompletedTimeout

A task did not complete within the specified timeout period.

PipelineError.ProcessTerminated

A task's process was terminated (e.g., by the OS or a signal).

PipelineError.InvalidTask

A task has invalid configuration (e.g., missing name, invalid command structure).

Execution Statistics Always Available

Execution statistics are always available, even on failure. This allows you to inspect what happened during the pipeline run, regardless of whether it succeeded or failed.

const result = await pipeline([task1, task2, task3]).run()

// Statistics are always available, even if result.ok is false
console.log("Total tasks:", result.stats.summary.total)
console.log("Completed:", result.stats.summary.completed)
console.log("Failed:", result.stats.summary.failed)
console.log("Skipped:", result.stats.summary.skipped)
console.log("Still running:", result.stats.summary.running)

// Inspect individual task results
for (const task of result.stats.tasks) {
  console.log(`${task.name}: ${task.status}`)
  if (task.status === "failed") {
    console.error("Error:", task.error?.message)
    console.error("Exit code:", task.exitCode)
  }
}

Related: Learn about Cancellation and Execution Statistics.