Title: Git node throws ENOENT: realpath '/files/git' — error path is truncated to parent directory, not the configured Repository Path

Describe the problem/error/question

Title: Git node throws ENOENT: realpath '/files/git' — error path is truncated to parent directory, not the configured Repository Path

Environment:

  • n8n version: [run docker exec -it n8n n8n --version to get the exact version]
  • Deployment: Self-hosted, Docker Compose, single container (n8nio/n8n:latest)
  • Database: [SQLite default, unless you changed it]
  • Node: Git node (n8n-nodes-base.git), Operation: Pull

Bug description:

The Git node fails with ENOENT: no such file or directory, realpath '/files/git' even though the configured Repository Path field is set to /files/git/ea2sa-blogs — a valid, existing repository, not /files/git. The error consistently references the parent directory of the configured path, never the full path actually entered in the field.

Steps to reproduce:

  1. Mount a host directory into the container at /files, with N8N_RESTRICT_FILE_ACCESS_TO=/files set
  2. Have a valid pre-existing git repository at /files/git/ea2sa-blogs (confirmed clean, in sync with origin, via direct git -C /files/git/ea2sa-blogs status run inside the container)
  3. Add a Git node, Operation = Pull, Repository Path = /files/git/ea2sa-blogs
  4. Execute the node

Expected behavior: Node performs git pull against /files/git/ea2sa-blogs.

Actual behavior: Node throws ENOENT: no such file or directory, realpath '/files/git' — note the path in the error is the parent directory, not the configured Repository Path.

Diagnostics already performed (ruling out config/permissions issues):

  • Confirmed /files/git/ea2sa-blogs/.git exists with a full valid structure (HEAD, config, objects, refs, etc.) via the Read/Write Files from Disk node
  • Confirmed the repo is healthy directly via docker exec -it n8n git -C /files/git/ea2sa-blogs status → clean working tree, up to date with origin/main
  • Confirmed origin remote is correctly set (git remote -v)
  • Confirmed the Repository Path field in the node UI literally displays /files/git/ea2sa-blogs (no expression binding, no trailing whitespace — retyped manually)
  • Confirmed N8N_RESTRICT_FILE_ACCESS_TO=/files is satisfied, since /files/git/ea2sa-blogs is a subpath
  • Ruled out volume-mount issues — directory ownership and contents verified correct from inside the running container

Stack trace:

Error: ENOENT: no such file or directory, realpath ‘/files/git’
at realpath (node:internal/fs/promises:1181:10)
at Object.resolvePath (…/n8n-core/…/file-system-helper-functions.ts:50:24)
at ExecuteContext.execute (…/nodes-base/nodes/Git/Git.node.ts:303:36)```

Possible related issue: This may share a root cause with #30538 (Git node clone path resolution throwing fatal: destination path '.' already exists on n8n 2.20.9) — both point to the Git node’s internal path resolution logic (likely in file-system-helper-functions.ts’s resolvePath) computing or validating against the parent directory instead of the full configured path.

Information on your n8n setup

  • **n8n version: 2.21.4
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

@Gary_Shepard the failing call is fs.realpath on the repo path — the Git node runs resolvePath before it ever touches git, and when realpath ENOENTs it recursively walks up the parents, thats why the error surfaces as /files/git and not your full path (it failed the full path first, then the parent). so the real question is why realpath ENOENTs on a path that exists.

the difference from your checks: git -C just chdirs, but realpath fully resolves every symlink in the path. run exactly what n8n runs, as the node user:

docker exec -u node n8n sh -c 'realpath /files/git/ea2sa-blogs'

if that reproduces the ENOENT, its at the fs level — most likely a symlink somewhere in your /files bind mount that resolves on the host but not inside the container. if realpath resolves fine standalone but the node still throws, then youre right that its the nodes path logic and its #30538 territory, worth the bug report with this exact repro.

That was helpful, however it did not solve the problem.

I’ve independently tested and ruled out every filesystem-level cause you could think of:

  • The directory absolutely exists.

  • Ownership and permissions are correct.

  • There are zero symlinks anywhere in the path on either the host or the container side.

  • When I invoke fs.realpath directly as the exact same user n8n runs as, it resolves the configured path perfectly.

  • Even Git itself operates on the repository natively without throwing a single issue.

My Diagnosis: The failure is isolated entirely to the Git node’s own resolvePath call.

I suspect it’s truncating or mis-resolving my configured path down to its parent directory before the realpath call even fires. That would explain exactly why the error consistently complains about the parent directory (/files/git) rather than the full, actual path I configured.

Steps to reproduce

  1. Mount a host directory into the container at /files, with N8N_RESTRICT_FILE_ACCESS_TO=/files set.
  2. Have a valid, pre-existing git repository at /files/git/ea2sa-blogs.
  3. Add a Git node, Operation = Pull, Repository Path = /files/git/ea2sa-blogs.
  4. Execute the node.

Expected behavior

Node performs git pull against /files/git/ea2sa-blogs.

Actual behavior

Error: ENOENT: no such file or directory, realpath ‘/files/git’
at realpath (node:internal/fs/promises:1181:10)
at Object.resolvePath (…/n8n-core/…/file-system-helper-functions.ts:50:24)
at ExecuteContext.execute (…/nodes-base/nodes/Git/Git.node.ts:303:36)

Note the error path is /files/git — the parent of the configured Repository Path, not the configured path itself (/files/git/ea2sa-blogs).

Repository confirmed healthy via git CLI, run directly inside the container:

bash

docker exec -it n8n git -C /files/git/ea2sa-blogs status
docker exec -it n8n git -C /files/git/ea2sa-blogs remote -v

Result: clean working tree, up to date with origin/main, remote correctly set to https://github.com/gwshepard58/ea2sa-blogs.git.
2. Directory structure confirmed via Read/Write Files from Disk node: .git directory present with full valid structure — HEAD, config, description, index, packed-refs, objects/, refs/, hooks/, info/, logs/.
3. fs.realpath tested directly as the exact user n8n runs as (node), against the configured path and every parent segment:

docker exec -u node n8n sh -c ’
for p in /files /files/git /files/git/ea2sa-blogs; do echo “— $p —”
ls -la “$p” 2>&1 | head -1
realpath “$p” 2>&1
done’

:sleepy_face: All three paths resolve cleanly with no errors. No symlinks present in the chain (directory entries throughout, confirmed via ls -la).

Bind mount source verified on the host — confirmed real directory, not a symlink: ls -la /opt/n8n-files readlink -f /opt/n8n-files

Yep… /opt/n8n-files is a genuine directory (drwxrwxr-x), owned by a normal host user (i.e., nothing to resolve — not a symlink).
confirmed to literally display /files/git/ea2sa-blogs, manually retyped to rule out hidden whitespace or stale cached values, no expression (fx) binding active

I wonder if there’s nothing left to test on the infrastructure side. This is feels like an n8n bug in the Git node’s path-resolution logic, not anything in my setup.

n8n --version is 2.21.4

@Gary_Shepard yeah that settles it — realpath resolving standalone as the node user with no symlinks rules out the fs layer completely. so its the Git node handing realpath a path thats already mangled down to the parent, exactly what youre seeing, and its on n8n not your setup. real bug, worth filing with this repro (its a clean one) alongside #30538.

to unblock now, skip the Git node since native git works fine for you — drop an Execute Command node running git -C /files/git/ea2sa-blogs pull. that runs git directly and never touches the nodes resolvePath. only catch is Execute Command has to be enabled in your build. if you want to stay on the Git node, one quick thing worth trying: toggle a trailing slash on the Repository Path, these dirname/basename truncations sometimes flip on that.