Skip to content

Installer/updater fails with "Invalid argument" on $XDG_CONFIG_HOME/git/config when .config/git is a symlink (directory or file) #6313

Description

@biotti

Existing issues matching what you're seeing

  • I was not able to find an open or closed issue matching what I'm seeing

Git for Windows version

git version 2.55.0.windows.2
cpu: x86_64
built from commit: c8dff95af823bd04181a583c03f3fbb954551c30
sizeof-long: 4
sizeof-size_t: 8
shell-path: D:/git-sdk-64-build-installers/usr/bin/sh
rust: disabled
feature: fsmonitor--daemon
gettext: enabled
libcurl: 8.21.0
OpenSSL: OpenSSL 3.5.7 9 Jun 2026
zlib: 1.3.2
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1

Windows version

Windows 11

Windows CPU architecture

x86_64 (64-bit)

Additional Windows version information

Microsoft Windows [Version 10.0.26200.8737]

Options set during installation

Editor Option: VIM
Custom Editor Path:
Default Branch Option: main
Path Option: Cmd
SSH Option: OpenSSH
Tortoise Option: false
CURL Option: OpenSSL
CRLF Option: CRLFAlways
Bash Terminal Option: MinTTY
Git Pull Behavior Option: Merge
Use Credential Manager: Enabled
Performance Tweaks FSCache: Disabled
Enable Symlinks: Enabled
Enable FSMonitor: Disabled

Other interesting things

Related to (but distinct from) #2530 — that issue is about HOMEDRIVE/HOMEPATH
pointing to an unreachable network drive; this issue is about a symlink at
.config/git (or .config/git/config) causing the same class of error
("Invalid argument") even when HOME/USERPROFILE are correctly and
consistently set to a local, always-reachable path.

Terminal/shell

Powershell and Bash

Commands that trigger the issue

Git-2.55.0.2-64-bit.exe

Expected behaviour

Install/upgrade without errors.

Actual behaviour

Summary

The Git for Windows installer/updater fails to set any system- or global-level
config value when the user's %USERPROFILE%\.config\git path (or the config
file inside it) is a native Windows symlink (created with mklink), even
though the symlink is fully valid, readable, and writable by normal Win32
tools (type, PowerShell, Explorer). The failure happens for every single
config key the installer/updater tries to set, and blocks the update from
completing.

Environment

  • Git for Windows version: 2.55.0.2 (64-bit)
  • OS: Windows 11
  • Previous Git version: worked correctly; this appears to be a regression
    introduced in a recent release (exact version where it started is not
    pinned down, but it used to work fine with the same symlink setup)

Setup

I manage my dotfiles (including Git config) in a Git repository, shared
between Windows and Linux, using symlinks. On Windows, this was originally
set up as:

  • C:\Users\<username>\.config\git → directory symlink (SYMLINKD,
    created via mklink /D) pointing into the dotfiles repository
  • containing a config file that is itself a symlink to the real
    gitconfig file tracked in the repository

Later, to isolate the issue, I restructured it to a single-level symlink:

  • C:\Users\<username>\.config\gitreal directory (not a symlink)
  • C:\Users\<username>\.config\git\config → file symlink (created via
    PowerShell New-Item -ItemType SymbolicLink) pointing to the real file
    in the dotfiles repository

Both configurations produce the identical failure.

fsutil reparsepoint query confirms these are genuine native Windows
symbolic links (Symbolic Link), not WSL-created reparse points, and
icacls shows full, correctly inherited permissions for both the user
account and SYSTEM ((I)(F)).

Steps to reproduce

  1. Set up %USERPROFILE%\.config\git\config as a Windows symlink (file or
    directory level) pointing to a real, valid, readable gitconfig file
    elsewhere on disk.
  2. Run the Git for Windows installer/updater (e.g. Git-2.55.0.2-64-bit.exe)
    to install or update Git.
  3. Observe that setting default config values fails repeatedly.

Expected behavior

The installer should be able to read/write configuration normally, following
the symlink transparently (as type, PowerShell, and Explorer all do).

Actual behavior

The installer shows one error popup per config key it attempts to set (17
occurrences observed in a single run), each with essentially the same
message, e.g.:
Unable to set system config "diff.astextplain.textconv":="astextplain": exit code 128

stderr:
warning: unable to access 'C:/Users/<username>/.config/git/config': Invalid argument
warning: unable to access 'C:/Users/<username>/.config/git/config': Invalid argument
fatal: unknown error occurred while reading the configuration files

This happens regardless of which config key is being set and regardless of
whether the invocation is --system or --global — every git.exe
invocation that needs to resolve the user-level config path fails at the
same early point, before ever reaching command-specific logic (confirmed via
GIT_TRACE/GIT_TRACE2: no built-in: trace line is ever emitted for the
failing invocations, meaning the failure happens during Git's generic
startup config read, not inside the config subcommand itself).

One of the 17 errors is a downstream consequence:

Line 3548: Could not reconfigure Scalar enlistments (output: , errors:
warning: unable to access 'C:/Users/Geraldo/.config/git/config' Invalid
argument, exit code: 128).

Diagnostic steps already performed

  • Verified HOME, USERPROFILE, and XDG_CONFIG_HOME/XDG_DATA_HOME/
    XDG_CACHE_HOME/XDG_STATE_HOME are all correctly and consistently set
    (tested both with Windows-style backslashes and with forward slashes;
    both produce the same failure, ruling out separator style as the cause).
  • Verified permissions via icacls on the symlink target: fully correct,
    no anomalies.
  • Confirmed manual invocation of git config --system --add ... and
    git config --global --add ... from both elevated and non-elevated
    shells succeeds without error — the failure only occurs during the
    installer's own execution of these commands.
  • Captured GIT_TRACE, GIT_TRACE2, and GIT_TRACE_SETUP during a real
    installer run: confirmed the failing git.exe processes never reach the
    point of logging built-in: config ..., i.e. the failure occurs during
    early config resolution/reading, before command dispatch.
  • Restructured the symlink from directory-level (SYMLINKD on .config\git)
    to file-level only (.config\git as a real directory, config as a file
    symlink): failure persists identically, ruling out directory-vs-file
    symlink type as the distinguishing factor — any symlink in that specific
    path triggers it.
  • Set GIT_CONFIG_GLOBAL (as a persistent System environment variable)
    pointing directly at the real target file, bypassing HOME/XDG
    resolution entirely. Confirmed via git config --list --show-origin
    that manual git invocations then correctly read/write only from
    C:/Program Files/Git/etc/gitconfig and the GIT_CONFIG_GLOBAL target,
    with zero references to .config/git/config. This fully confirmed
    the root cause. However, even after a full system reboot (to rule out
    stale environment propagation, e.g. via services started before login),
    the installer/updater still failed referencing .config/git/config
    — suggesting the installer's own internal update logic does not (fully)
    respect GIT_CONFIG_GLOBAL in the same way plain git invocations do.

Workaround found

Renaming/removing the symlinked .config\git directory (so that Git falls
back to ~/.gitconfig instead of the XDG-style path) allows the
installer/updater to complete successfully with no errors. This confirms
the symlink traversal itself (not permissions, not environment variables,
not separator style) is the root cause.

Suggested areas to investigate

  • Whether recent hardening around symlink handling (related to symlink-based
    attack CVE fixes in recent Git releases) affects path resolution for the
    XDG config file specifically on Windows via the mingw compat layer
    (compat/mingw.c), causing stat/open-equivalent calls to return
    ERROR_INVALID_PARAMETER when traversing a reparse point at that
    specific location.
  • Why GIT_CONFIG_GLOBAL correctly bypasses the issue for manual git
    invocations but does not appear to be honored (or is not being inherited)
    by the installer/updater's own internal config-setting routine.

Happy to provide the full GIT_TRACE2 logs, screenshots of all 17 error
dialogs, and further system details if useful.

Repository

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions