When propagating errors through promise rejections, catch blocks, or error transformations, always preserve the original error information to maintain debugging context and error traceability.

Common anti-patterns include:

Good practices:

// โœ… Pass the original error when rejecting
} catch (error) {
  this.#setTransactionEnded(true, error);
  reject(error); // Preserve the original error
}

// โœ… Handle unknown error types safely while preserving info
function makeAppErrorResponse(m: Mutation, e: unknown): MutationResponse {
  return {
    result: {
      error: 'app',
      details: e instanceof Error ? e.message : String(e), // Convert unknown to string
    }
  };
}

// โœ… Return rejected promises instead of sync throws in async contexts
run(): Promise<HumanReadable<TReturn>> {
  return Promise.reject(new Error('AuthQuery cannot be run'));
}

This practice significantly improves debugging experience by maintaining the full error chain and context, making it easier to trace issues back to their root cause.