or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

dmg-building.mddmg-customization.mderror-handling.mdindex.mdlicense-management.mdvolume-operations.md
tile.json

error-handling.mddocs/

Error Handling

Robust error handling for hdiutil operations with detailed error explanations and retry logic for transient failures. Provides comprehensive support for managing macOS disk utility operations and DMG-related errors.

Capabilities

hdiutil Command Wrapper

High-level wrapper for hdiutil commands with automatic retry logic and error handling.

/**
 * Execute hdiutil command with retry logic for transient errors
 * Automatically retries failed operations for known transient error codes
 * @param args - Array of hdiutil command arguments
 * @returns Promise resolving to command output or null
 * @throws Error for non-transient failures after retry attempts
 */
function hdiUtil(args: string[]): Promise<string | null>;

Usage Examples:

import { hdiUtil } from "dmg-builder";

// Attach DMG with automatic retry
const attachResult = await hdiUtil([
  "attach", "-noverify", "-noautoopen", "/path/to/file.dmg"
]);

// Create DMG from folder
await hdiUtil([
  "create", "-srcfolder", "/source/folder", 
  "-format", "UDZO", "/output/file.dmg"
]);

// Get detailed system information
const info = await hdiUtil(["info"]);

// Detach volume with retry logic
await hdiUtil(["detach", "-quiet", "/dev/disk2"]);

Error Code Explanation

Comprehensive error code explanation system for hdiutil operations.

/**
 * Provide human-readable explanation for hdiutil error codes
 * Includes common causes and suggested solutions
 * @param errorCode - Numeric error code from hdiutil
 * @returns Descriptive error message with troubleshooting guidance
 */
function explainHdiutilError(errorCode: number): string;

Usage Examples:

import { explainHdiutilError } from "dmg-builder";

try {
  await hdiUtil(["attach", "/invalid/path.dmg"]);
} catch (error) {
  const explanation = explainHdiutilError(error.code);
  console.error(`hdiutil failed: ${explanation}`);
  // Output: "No such file or directory: Check if the specified path exists."
}

// Get explanation for specific error codes
console.log(explainHdiutilError(16)); 
// "Resource busy: The volume is in use. Try closing files or processes and retry."

console.log(explainHdiutilError(35));
// "Operation timed out: The system was too slow or unresponsive. Try again."

Transient Error Detection

Set of error codes that indicate transient failures suitable for automatic retry.

/**
 * Set of hdiutil error codes that are transient and should be retried
 * These codes typically indicate temporary resource conflicts or timing issues
 */
const hdiutilTransientExitCodes: Set<number>;

Transient Error Codes:

  • 1 - Generic error (can occur from brief race conditions)
  • 16 - Resource busy (volume in use, often resolves after retry)
  • 35 - Operation timed out (system delay, retry after short delay)
  • 256 - Volume in use or unmount failure (similar to 16)
  • 49153 - Volume not mounted yet (attach may be too fast)

Error Code Reference

Complete Error Code Mappings

/**
 * Comprehensive mapping of hdiutil error codes to explanations
 * Covers common and uncommon hdiutil failure scenarios
 */
const errorCodeMappings = {
  0: "Success: The hdiutil command completed without error.",
  1: "Generic error: The operation failed, but the reason is not specific. Check command syntax or permissions.",
  2: "No such file or directory: Check if the specified path exists.",
  6: "Disk image to resize is not currently attached or not recognized as a valid block device by macOS.",
  8: "Exec format error: The file might not be a valid disk image.",
  16: "Resource busy: The volume is in use. Try closing files or processes and retry.",
  22: "Invalid argument: One or more arguments passed to hdiutil are incorrect.",
  35: "Operation timed out: The system was too slow or unresponsive. Try again.",
  36: "I/O error: There was a problem reading or writing to disk. Check disk health.",
  100: "Image-related error: The disk image may be corrupted or invalid.",
  256: "Volume is busy or could not be unmounted. Try again after closing files.",
  49153: "Volume not mounted yet: The image may not have been fully attached.",
  "-5341": "Disk image too small: hdiutil could not fit the contents. Increase the image size.",
  "-5342": "Specified size too small: Disk image creation failed due to insufficient size."
};

Error Categories

Temporary/Retryable Errors:

  • Resource conflicts (busy volumes, locked files)
  • Timing issues (operations too fast, timeouts)
  • System load issues (high resource usage)

Permanent/Non-Retryable Errors:

  • File not found or permission denied
  • Corrupted disk images
  • Invalid command syntax
  • Hardware failures

Retry Configuration

Retry Strategy Parameters

The retry system uses exponential backoff with configurable parameters:

const retryConfig = {
  retries: 5,           // Maximum retry attempts
  interval: 5000,       // Initial delay (5 seconds)
  backoff: 2000,        // Additional delay per retry (2 seconds)
  shouldRetry: (error) => hdiutilTransientExitCodes.has(error.code)
};

Custom Retry Logic

// Internal implementation of retry logic
const shouldRetry = (args: string[]) => (error: any) => {
  const code = error.code ?? -1;
  const stderr = error.stderr?.toString() || "";
  const stdout = error.stdout?.toString() || "";
  const output = `${stdout} ${stderr}`.trim();

  const willRetry = hdiutilTransientExitCodes.has(code);
  
  // Log retry decision with context
  log.warn({ 
    willRetry, 
    args, 
    code, 
    output 
  }, `hdiutil error: ${explainHdiutilError(code)}`);

  return willRetry;
};

Debugging and Diagnostics

Debug Mode

Enable verbose hdiutil output for troubleshooting:

// Set environment variable for debug output
process.env.DEBUG_DMG = "true";

// This adds -verbose flag to hdiutil commands instead of -quiet
// Provides detailed operation logs for diagnosis

Common Error Scenarios and Solutions

Volume Busy (Code 16, 256):

// Problem: Volume is in use by another process
// Solution: Automatic retry with delay, eventually force detach

await hdiUtil(["detach", "-quiet", devicePath])
  .catch(async (error) => {
    if (hdiutilTransientExitCodes.has(error.code)) {
      await new Promise(resolve => setTimeout(resolve, 3000));
      return hdiUtil(["detach", "-force", devicePath]);
    }
    throw error;
  });

Timeout Issues (Code 35):

// Problem: System overloaded or slow disk operations
// Solution: Automatic retry with exponential backoff
// The retry system handles this automatically

File Not Found (Code 2):

// Problem: Invalid DMG path or missing file
// Solution: Validate paths before hdiutil operations

import { existsSync } from "fs";

if (!existsSync(dmgPath)) {
  throw new Error(`DMG file not found: ${dmgPath}`);
}

Disk Image Too Small (Code -5341, -5342):

// Problem: Insufficient space allocated for DMG contents
// Solution: Calculate required size and recreate with larger size

// This typically requires recreating the DMG with proper size calculation
const requiredSize = calculateContentSize(sourceFolder);
const dmgSize = Math.ceil(requiredSize * 1.1); // 10% padding

Logging and Monitoring

The error handling system provides comprehensive logging:

// Error logs include:
// - Original hdiutil command and arguments
// - Error code and stderr/stdout output
// - Retry decision and timing
// - Final outcome (success/failure)

log.warn({
  command: "hdiutil",
  args: ["attach", "/path/to/file.dmg"],
  exitCode: 16,
  stderr: "hdiutil: attach failed - Resource busy",  
  willRetry: true,
  retryAttempt: 2
}, "hdiutil operation failed, retrying...");

Integration with Volume Operations

Error handling is tightly integrated with volume operations:

  • attachAndExecute: Uses hdiUtil with automatic retry for attach operations
  • detach: Implements custom retry logic with force fallback for stubborn volumes
  • Volume detection: Handles mount path detection failures gracefully

This comprehensive error handling ensures reliable DMG operations even in challenging system conditions.