Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

pi/agent/skillz/windows-filename-guidance/SKILL.md

Raw
Rendered preview

name: windows-filename-guidance os: win32 description: "Use for Windows filename rules and failures involving reserved DOS device names such as NUL, invalid or trailing characters, inaccessible cross-platform names, long paths, or files that Windows cannot inspect, rename, or delete normally."

Windows Filename Guidance

Model

Separate three layers:

  1. the filesystem's on-disk rules
  2. Windows path namespace and normalization
  3. the application's API and path handling

A name can exist on NTFS yet be unreachable through an ordinary Win32 path. This commonly happens when another operating system, namespace, share, or tool created it. It does not by itself prove corruption or bad permissions.

Normal Windows Names

For portable Windows names:

  • Do not use control characters U+0000 through U+001F or <>:"/\|?*.
  • Do not end a component with a space or period; Win32 normally strips them.
  • Do not use . or .. as components.
  • Do not use CON, PRN, AUX, NUL, COM1 through COM9, or LPT1 through LPT9, regardless of case. An extension does not help: NUL.txt still resolves as NUL.
  • Treat : as the drive/alternate-data-stream separator, not filename text.
  • Assume case-insensitive lookup even though case is preserved.
  • Keep each component within the filesystem limit. Path-length support depends on the filesystem, API, application, and whether long paths are enabled.

Use these rules when generating archives, repositories, build output, or shared files intended for Windows.

Diagnose

  1. Get the exact absolute path, error text, filesystem, and whether the target is a file or directory.

  2. Enumerate the parent rather than probing the suspect name directly:

    cmd.exe /d /c dir /a /x "C:\absolute\parent"
    
  3. Classify the failure:

    • reserved device name
    • trailing space/period or other Win32-invalid name
    • path too long for the current tool
    • open handle
    • ACL/ownership denial
    • filesystem corruption supported by disk or event-log errors
  4. Prefer the same namespace, share, or program that created the entry when it can safely rename or remove it.

Never use Test-Path NUL or if exist NUL to prove a file exists; those can address the device.

Extended Paths

For an existing local entry that ordinary Win32 normalization cannot address, use an exact absolute extended path:

\\?\C:\absolute\path

For UNC paths, convert \\server\share\path to:

\\?\UNC\server\share\path

The \\?\ prefix bypasses ordinary Win32 parsing; it does not bypass the filesystem's rules, ACLs, locks, or corruption. Do not combine it with a relative path.

Git Bash Boundary

Git Bash/MSYS2 rewrites path-looking arguments passed to native Windows executables. This can also affect shell program text passed as one argument, such as a remote command containing 2>/dev/null. Preserve literal POSIX text at that boundary with the documented exclusion variable:

MSYS2_ARG_CONV_EXCL='*' podman.exe machine ssh 'command 2>/dev/null'

Do not assume MSYS_NO_PATHCONV covers every native executable.

Bash double quotes reduce a leading \\ to \. Therefore "\\?\C:\absolute\path\NUL" reaches a child process as the malformed \?\C:\absolute\path\NUL. When invoking cmd.exe from Bash, preserve the literal path and disable MSYS2 argument conversion:

MSYS2_ARG_CONV_EXCL='*' cmd.exe /d /c del '\\?\C:\absolute\path\NUL'

The cmd examples below are native cmd.exe syntax. Do not paste their quoted paths unchanged into a Bash command.

Delete Problem Entries

Show the exact target and obtain explicit confirmation first. Reject wildcards, relative paths, drive/share roots, and ambiguous targets.

If Git Bash created the entry and can enumerate its exact absolute POSIX path, prefer removing it through that same layer:

rm -- "$PWD/NUL"

File through native cmd.exe:

cmd.exe /d /c del "\\?\C:\absolute\path\NUL"

Read-only file, only after reporting why:

cmd.exe /d /c del /f "\\?\C:\absolute\path\NUL"

Empty directory:

cmd.exe /d /c rd "\\?\C:\absolute\path\NUL"

Nonempty directory: enumerate its contents and obtain separate recursive-delete confirmation before running:

cmd.exe /d /c rd /s /q "\\?\C:\absolute\path\NUL"

Preserve exact spelling, extension, spaces, and periods. Enumerate the parent again; report success only when the literal entry is absent.

git status is not direct filesystem verification. If deletion succeeds and parent enumeration no longer contains the literal entry, but Git still reports it, bypass its filesystem monitor and untracked cache once:

git -c core.fsmonitor=false -c core.untrackedCache=false status --short

If that removes the report and the repository uses the built-in monitor, restart it. Do not issue further deletion commands solely because cached Git status still names the entry.

If the extended path still fails, use dir /a /x on the parent. Verify the 8.3 short name maps to the exact target before proposing deletion through that name.

Other Causes

  • Open handle: identify and close the owning process before retrying.
  • ACL denial: inspect ownership and permissions; request elevation or ownership changes only when confirmed necessary.
  • Deep path: use an extended path, shorten a parent, map a deeper drive/share, or use a tool that supports the full path.
  • Corruption: run filesystem repair only when independent evidence supports it; an odd filename alone is insufficient.
  • Missing parent entry: stop. A successful reference to NUL alone is the null device, not evidence of a file.

Sources

---
name: windows-filename-guidance
os: win32
description: "Use for Windows filename rules and failures involving reserved DOS device names such as NUL, invalid or trailing characters, inaccessible cross-platform names, long paths, or files that Windows cannot inspect, rename, or delete normally."
---

# Windows Filename Guidance

## Model

Separate three layers:

1. the filesystem's on-disk rules
2. Windows path namespace and normalization
3. the application's API and path handling

A name can exist on NTFS yet be unreachable through an ordinary Win32 path.
This commonly happens when another operating system, namespace, share, or tool
created it. It does not by itself prove corruption or bad permissions.

## Normal Windows Names

For portable Windows names:

- Do not use control characters U+0000 through U+001F or `<>:"/\|?*`.
- Do not end a component with a space or period; Win32 normally strips them.
- Do not use `.` or `..` as components.
- Do not use `CON`, `PRN`, `AUX`, `NUL`, `COM1` through `COM9`, or `LPT1`
  through `LPT9`, regardless of case. An extension does not help: `NUL.txt`
  still resolves as `NUL`.
- Treat `:` as the drive/alternate-data-stream separator, not filename text.
- Assume case-insensitive lookup even though case is preserved.
- Keep each component within the filesystem limit. Path-length support depends
  on the filesystem, API, application, and whether long paths are enabled.

Use these rules when generating archives, repositories, build output, or shared
files intended for Windows.

## Diagnose

1. Get the exact absolute path, error text, filesystem, and whether the target
   is a file or directory.
2. Enumerate the parent rather than probing the suspect name directly:

   ```cmd
   cmd.exe /d /c dir /a /x "C:\absolute\parent"
   ```

3. Classify the failure:
   - reserved device name
   - trailing space/period or other Win32-invalid name
   - path too long for the current tool
   - open handle
   - ACL/ownership denial
   - filesystem corruption supported by disk or event-log errors
4. Prefer the same namespace, share, or program that created the entry when it
   can safely rename or remove it.

Never use `Test-Path NUL` or `if exist NUL` to prove a file exists; those can
address the device.

## Extended Paths

For an existing local entry that ordinary Win32 normalization cannot address,
use an exact absolute extended path:

```text
\\?\C:\absolute\path
```

For UNC paths, convert `\\server\share\path` to:

```text
\\?\UNC\server\share\path
```

The `\\?\` prefix bypasses ordinary Win32 parsing; it does not bypass the
filesystem's rules, ACLs, locks, or corruption. Do not combine it with a
relative path.

## Git Bash Boundary

Git Bash/MSYS2 rewrites path-looking arguments passed to native Windows
executables. This can also affect shell program text passed as one argument,
such as a remote command containing `2>/dev/null`. Preserve literal POSIX text
at that boundary with the documented exclusion variable:

```bash
MSYS2_ARG_CONV_EXCL='*' podman.exe machine ssh 'command 2>/dev/null'
```

Do not assume `MSYS_NO_PATHCONV` covers every native executable.

Bash double quotes reduce a leading `\\` to `\`. Therefore
`"\\?\C:\absolute\path\NUL"` reaches a child process as the malformed
`\?\C:\absolute\path\NUL`. When invoking `cmd.exe` from Bash, preserve the
literal path and disable MSYS2 argument conversion:

```bash
MSYS2_ARG_CONV_EXCL='*' cmd.exe /d /c del '\\?\C:\absolute\path\NUL'
```

The `cmd` examples below are native `cmd.exe` syntax. Do not paste their quoted
paths unchanged into a Bash command.

## Delete Problem Entries

Show the exact target and obtain explicit confirmation first. Reject wildcards,
relative paths, drive/share roots, and ambiguous targets.

If Git Bash created the entry and can enumerate its exact absolute POSIX path,
prefer removing it through that same layer:

```bash
rm -- "$PWD/NUL"
```

File through native `cmd.exe`:

```cmd
cmd.exe /d /c del "\\?\C:\absolute\path\NUL"
```

Read-only file, only after reporting why:

```cmd
cmd.exe /d /c del /f "\\?\C:\absolute\path\NUL"
```

Empty directory:

```cmd
cmd.exe /d /c rd "\\?\C:\absolute\path\NUL"
```

Nonempty directory: enumerate its contents and obtain separate recursive-delete
confirmation before running:

```cmd
cmd.exe /d /c rd /s /q "\\?\C:\absolute\path\NUL"
```

Preserve exact spelling, extension, spaces, and periods. Enumerate the parent
again; report success only when the literal entry is absent.

`git status` is not direct filesystem verification. If deletion succeeds and
parent enumeration no longer contains the literal entry, but Git still reports
it, bypass its filesystem monitor and untracked cache once:

```bash
git -c core.fsmonitor=false -c core.untrackedCache=false status --short
```

If that removes the report and the repository uses the built-in monitor,
restart it. Do not issue further deletion commands solely because cached Git
status still names the entry.

If the extended path still fails, use `dir /a /x` on the parent. Verify the 8.3
short name maps to the exact target before proposing deletion through that name.

## Other Causes

- Open handle: identify and close the owning process before retrying.
- ACL denial: inspect ownership and permissions; request elevation or ownership
  changes only when confirmed necessary.
- Deep path: use an extended path, shorten a parent, map a deeper drive/share,
  or use a tool that supports the full path.
- Corruption: run filesystem repair only when independent evidence supports it;
  an odd filename alone is insufficient.
- Missing parent entry: stop. A successful reference to `NUL` alone is the
  null device, not evidence of a file.

## Sources

- [MSYS2: filesystem paths and argument conversion](https://www.msys2.org/docs/filesystem-paths/)
- [GNU Bash: double-quote backslash handling](https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html)
- [Git: built-in filesystem monitor](https://git-scm.com/docs/git-fsmonitor--daemon)
- [Super User: removing a file named NUL](https://superuser.com/questions/282194/how-do-i-remove-a-file-named-nul-on-windows)
- [Microsoft KB 320081: undeletable NTFS entries](https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/cannot-delete-file-folder-on-ntfs-file-system)
- [Microsoft: Naming Files, Paths, and Namespaces](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file)
- [Wikipedia: filename limitations](https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations)