JanDeDobbeleer/oh-my-posh · error

unable to remove existing font file: %s

Error message

unable to remove existing font file: %s

What it means

Before writing the new font file, the Windows `install` routine removes a previously installed font with the same filename. This error wraps `os.Remove(fullPath)` failing on that existing file — typically because the font is locked by a running application or the process lacks delete permission.

Source

Thrown at src/cli/font/install_windows.go:45

	fontsDir := filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local", "Microsoft", "Windows", "Fonts")

	log.Debugf("installing font %s to %s", font.FileName, fontsDir)

	// check if the Fonts folder exists, if not, create it
	if _, err := os.Stat(fontsDir); os.IsNotExist(err) {
		if err = os.MkdirAll(fontsDir, 0755); err != nil {
			return fmt.Errorf("unable to create fonts directory: %s", err.Error())
		}
	}

	log.Debug("fonts directory exists, proceeding with installation")

	fullPath := filepath.Join(fontsDir, font.FileName)
	// validate if the font is already installed, remove it in case it is
	if _, err := os.Stat(fullPath); err == nil {
		log.Debugf("font %s already exists, removing it", fullPath)
		if err = os.Remove(fullPath); err != nil {
			return fmt.Errorf("unable to remove existing font file: %s", err.Error())
		}
	}

	log.Debugf("writing font file to %s", fullPath)

	err := os.WriteFile(fullPath, font.Data, 0644)
	if err != nil {
		return fmt.Errorf("unable to write font file: %s", err.Error())
	}

	log.Debug("font file written successfully, proceeding with registry entry")

	// Add registry entry
	reg := registry.CURRENT_USER
	regValue := fullPath

	log.Debug("opening HKEY_CURRENT_USER for writing (SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts)")

View on GitHub (pinned to 0976794618)

Solutions

  1. Close applications using the font (browsers, editors, the terminal itself) and retry
  2. Run elevated if the existing file is in C:\Windows\Fonts
  3. Clear the read-only attribute: `attrib -r "<fullPath>"` then retry
  4. Reboot (or restart explorer.exe) to release GDI font handles, then reinstall
  5. Manually delete the old font via Settings > Fonts, then run the installer again

Example fix

// before
PS> oh-my-posh font install FiraCode   # file locked by running app
// after: close apps / release handle, or
PS> attrib -r "$env:LOCALAPPDATA\Microsoft\Windows\Fonts\FiraCode-Regular.ttf"
PS> oh-my-posh font install FiraCode
Defensive patterns

Strategy: validation

Validate before calling

# pre-check that the existing font file is deletable
$path = "$env:LOCALAPPDATA\Microsoft\Windows\Fonts\FiraCode-Regular.ttf"
if (Test-Path $path) {
  try { $fs=[IO.File]::Open($path,'Open','ReadWrite','None'); $fs.Close() }
  catch { Write-Error "Font file is locked: $path"; exit 1 }
}

Prevention

When it happens

Trigger: `os.Stat(fullPath)` succeeds (font already installed), then `os.Remove(fullPath)` returns an error: file in use (loaded by GDI/DirectWrite in a running app), read-only attribute, or ACL denies delete (machine fonts dir without elevation).

Common situations: Reinstalling/upgrading a font that Word/Photoshop/your terminal currently has loaded; SYSTEM-owned file in C:\Windows\Fonts installed by another admin; read-only attribute set on the old file.

Related errors


AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31). Data as JSON: /api/errors/9d97b9dae2682419. Report an issue: GitHub.