gastownhall/beads · error

checking sidecar destination %s: %w

Error message

checking sidecar destination %s: %w

What it means

This wraps an os.Stat failure on the retire op's destination path during retirement (after the source was confirmed to exist). Any stat error other than a clean not-exist aborts the retire step. Because this happens during apply, it can leave the migration partially applied.

Source

Thrown at cmd/bd/migrate_hooks_apply.go:393

			)
		}
	}

	return nil
}

func retireHookSidecar(op hookMigrationRetireOp) (string, error) {
	sourceExists, err := pathExists(op.SourcePath)
	if err != nil {
		return "", fmt.Errorf("checking sidecar %s: %w", op.SourcePath, err)
	}
	if !sourceExists {
		return "", nil
	}

	destinationExists, err := pathExists(op.DestinationPath)
	if err != nil {
		return "", fmt.Errorf("checking sidecar destination %s: %w", op.DestinationPath, err)
	}

	if destinationExists {
		equal, err := filesEqual(op.SourcePath, op.DestinationPath)
		if err != nil {
			return "", fmt.Errorf("comparing sidecar %s to %s: %w", op.SourcePath, op.DestinationPath, err)
		}
		if !equal {
			return "", fmt.Errorf("artifact collision for %s: %s already exists with different content", op.SourcePath, op.DestinationPath)
		}
		if err := os.Remove(op.SourcePath); err != nil {
			return "", fmt.Errorf("removing already-retired sidecar %s: %w", op.SourcePath, err)
		}
		return op.SourcePath + " -> " + op.DestinationPath + " (destination already existed)", nil
	}

	if err := os.Rename(op.SourcePath, op.DestinationPath); err != nil {
		return "", fmt.Errorf("retiring sidecar %s -> %s: %w", op.SourcePath, op.DestinationPath, err)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error; run `ls -ld` on the destination path and its parents to find the failure point.
  2. Fix directory permissions or remount the volume holding the destination.
  3. Re-run `bd migrate`; retire ops skip sources that no longer exist, so retries are safe.

Example fix

// before
$ bd migrate
checking sidecar destination /mnt/ext/bd/x.sh: input/output error
// after
$ sudo mount /mnt/ext && bd migrate
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(filepath.Dir(op.DestinationPath)); err != nil {
	return fmt.Errorf("destination dir not accessible: %w", err)
}

Try / catch

if _, err := retireHookSidecar(op); err != nil {
	var pe *fs.PathError
	if errors.As(err, &pe) && !errors.Is(pe.Err, os.ErrNotExist) {
		return fmt.Errorf("destination sidecar %s unusable: %w", pe.Path, pe.Err)
	}
	return err
}

Prevention

When it happens

Trigger: During `bd migrate` apply, destinationExists check fails: destination parent directory permission-denied, destination on an unmounted volume, or an I/O error from the filesystem.

Common situations: Destination directory created with restrictive mode by an earlier run; external drive/NFS share disconnected between validation and apply; user switched effective uid via sudo mid-session.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d85b96ccc90f3937. Report an issue: GitHub.