shadcn-ui/ui · error · ApplyWorkspaceSyncError

Failed to sync linked workspace configs. ${error.message}

Error message

Failed to sync linked workspace configs. ${error.message}

What it means

Wrap-around error thrown at the end of the apply workspace-sync catch block. Any exception inside the sync try — a FileBackupError (#20), fs.writeJson failing with ENOSPC/EACCES, or JSON serialization failure — triggers restoreFileBackup for every workspace in reverse order, then rethrows as ApplyWorkspaceSyncError with the original cause appended.

Source

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

          ...patch,
          tailwind: {
            ...workspaceConfig.existingConfig.tailwind,
            ...patch.tailwind,
          },
        },
        { spaces: 2 }
      )
    }

    for (const workspaceConfig of workspaceConfigs) {
      deleteFileBackup(workspaceConfig.configPath)
    }
  } catch (error) {
    for (const workspaceConfig of [...workspaceConfigs].reverse()) {
      restoreFileBackup(workspaceConfig.configPath)
    }

    throw new ApplyWorkspaceSyncError(
      `Failed to sync linked workspace configs.${error instanceof Error ? ` ${error.message}` : ""}`
    )
  }
}

async function getApplyWorkspaceConfigs(config: Config) {
  const workspaceConfig = await getWorkspaceConfig(config)
  if (!workspaceConfig) {
    return []
  }

  const linkedConfigs = new Map<string, Config>()

  for (const linkedConfig of Object.values(workspaceConfig)) {
    if (linkedConfig.resolvedPaths.cwd === config.resolvedPaths.cwd) {
      continue
    }

View on GitHub (pinned to efac598707)

Solutions

  1. Read the original cause after the period in the message — it tells you which workspace and why.
  2. Free disk space on the volume holding the linked workspaces (ENOSPC).
  3. Check write permissions on every workspace's components.json, not just the root.
  4. Verify each linked workspace path declared in components.json still exists and is writable.
  5. If the rollback did not complete, manually restore each components.json from its components.json.bak.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before apply: assert every workspace config path is writable, not just root.
import fs from "fs-extra"

async function preflightWorkspaceWrite(configPaths: string[]) {
  for (const p of configPaths) {
    await fs.access(p, fs.constants.W_OK)
  }
}

Type guard

function isApplyWorkspaceSyncError(e: unknown): e is Error {
  return e instanceof Error && e.name === "ApplyWorkspaceSyncError"
}

Try / catch

try {
  await applyToWorkspaces(config)
} catch (e) {
  if (isApplyWorkspaceSyncError(e)) {
    // The underlying cause is appended after the period in e.message.
    // Backups were already restored in reverse; verify each config:
    for (const p of workspaceConfigPaths) {
      const bak = `${p}.bak`
      if (await fs.pathExists(bak)) await fs.move(bak, p, { overwrite: true })
    }
  }
  throw e
}

Prevention

When it happens

Trigger: Any exception raised inside apply.ts:482-507 during linked-workspace config sync. The appended `error.message` identifies the underlying cause: disk full (ENOSPC), permission denied writing a workspace components.json, or a FileBackupError from #20.

Common situations: Disk full mid-write, one linked workspace on a network mount that drops, components.json made read-only after the initial readJson, partial state from a previous crashed apply, monorepo with one package on a clean git checkout (read-only).

Related errors


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