shadcn-ui/ui · error · FileBackupError

Could not back up .

Error message

Could not back up ${filePath}.

What it means

Thrown as a FileBackupError when createFileBackup cannot rename a linked workspace's components.json to its .bak sidecar during `shadcn apply`. The apply command backs up every linked workspace config before writing the style/tailwind patch; if the atomic rename inside createFileBackup throws, it returns null and the sync aborts with this error.

Solutions

  1. Verify write permissions on each linked workspace's components.json AND its parent directory (chmod/chown).
  2. Ensure the working directory and the target config are on the same filesystem (avoid separate /tmp mounts).
  3. Remove any stale components.json.bak files left by a previous crashed run before retrying.
  4. Disable file-locking antivirus / pause IDE watchers for the project directory and retry.
  5. Re-run with adequate privileges and a writable working tree (not a read-only container filesystem).
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight before calling the apply sync path: ensure each linked
// workspace's components.json is on a writable directory.
import fs from "fs-extra"
import path from "path"

async function assertBackupCapable(configPaths: string[]) {
  for (const p of configPaths) {
    const dir = path.dirname(p)
    try {
      await fs.access(dir, fs.constants.W_OK | fs.constants.R_OK)
    } catch {
      throw new Error(`Directory not writable for backup: ${dir}`)
    }
    // Stale .bak from a prior crashed run will block rename on some FSes.
    const bak = `${p}.bak`
    if (await fs.pathExists(bak)) await fs.remove(bak)
  }
}

Type guard

import fs from "fs-extra"
import path from "path"

function isBackupCapableSync(filePath: string): boolean {
  try {
    fs.accessSync(path.dirname(filePath), fs.constants.W_OK | fs.constants.R_OK)
    return true
  } catch {
    return false
  }
}

Try / catch

try {
  await applyWorkspaceSync(config)
} catch (e) {
  if (e instanceof Error && /Could not back up/.test(e.message)) {
    // e.filePath (FileBackupError) holds the offending config path.
    const offending = (e as { filePath?: string }).filePath
    console.error(`Backup failed for ${offending}. Check permissions / clean .bak files.`)
  }
  throw e
}

Prevention

When it happens

Trigger: Running `shadcn apply` in a monorepo where getWorkspaceConfig yields linked configs, and createFileBackup returns null for one of them. The existence of components.json is already verified at apply.ts:472, so null here means fsExtra.renameSync threw — EPERM/EACCES, EXDEV (cross-device rename), or the file vanished between the exists-check and the rename.

Common situations: Read-only filesystem (CI runner, container with read-only mount), permission denied on components.json or its parent dir, /tmp or workspace on a different mount than the project (EXDEV), a stale .bak held open by another process, antivirus/file-locking on Windows, IDE watcher holding the file.

Related errors


AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12). Data as JSON: /api/errors/7ba0916f3394b861. Report an issue: GitHub.

Appendix: source

Thrown at packages/shadcn/src/commands/apply.ts:486

    const configPath = path.resolve(
      linkedConfig.resolvedPaths.cwd,
      "components.json"
    )
    if (!(await fs.pathExists(configPath))) {
      continue
    }

    workspaceConfigs.push({
      configPath,
      existingConfig: await fs.readJson(configPath),
    })
  }

  try {
    for (const workspaceConfig of workspaceConfigs) {
      const backupPath = createFileBackup(workspaceConfig.configPath)
      if (!backupPath) {
        throw new FileBackupError(workspaceConfig.configPath)
      }
    }

    for (const workspaceConfig of workspaceConfigs) {
      await fs.writeJson(
        workspaceConfig.configPath,
        {
          ...workspaceConfig.existingConfig,
          ...patch,
          tailwind: {
            ...workspaceConfig.existingConfig.tailwind,
            ...patch.tailwind,
          },
        },
        { spaces: 2 }
      )
    }

View on GitHub (pinned to efac598707)