abhigyanpatwari/GitNexus · error

"path" must be an absolute path

Error message

"path" must be an absolute path

What it means

HTTP 400 returned by POST /api/analyze when "path" is present but relative (fails path.isAbsolute). The server refuses to resolve relative paths itself — resolution would depend on the server's cwd — and deliberately does not stat or realpath user input in-route (rejected as a user-controlled filesystem probe), so it demands an already-absolute path and lets the analyze worker surface a clean error if the directory does not exist.

Source

Thrown at gitnexus/src/server/api.ts:1540

        if (tokenError) {
          res.status(tokenError.status).json({ error: tokenError.error });
          return;
        }

        // Path validation. The previous `normalize !== resolve` guard was inert
        // (both collapse `..` identically) and only false-rejected trailing
        // slashes, so it is dropped. Analyzing a local path the operator names
        // is the tool's intended capability (same as the CLI); the dangerous
        // part was cross-origin reach, which is closed by requireTrustedOrigin
        // on this route (scoped to loopback, the server's own bound host, and a
        // configured GITNEXUS_PUBLIC_ORIGIN — other LAN devices are NOT
        // trusted). We only require an absolute path here and
        // let the analyze worker surface a clear error if it does not exist.
        // (We do NOT realpath/stat the path in-route: that would be a
        // user-controlled filesystem read — CodeQL js/path-injection — for no
        // security gain.)
        if (repoLocalPath && !path.isAbsolute(repoLocalPath)) {
          res.status(400).json({ error: '"path" must be an absolute path' });
          return;
        }

        const job = jobManager.createJob({ repoUrl, repoPath: repoLocalPath });

        // If job was already running (dedup), just return its id. The token is
        // not part of the dedup identity and is never stored on the job, so a
        // token on THIS request had no effect — the existing job already
        // cloned (or is cloning) with whatever credentials its originating
        // request supplied. Surface `tokenIgnored` so an authenticated caller
        // isn't misled into thinking their PAT took effect on a reused job.
        if (job.status !== 'queued') {
          const body: { jobId: string; status: string; tokenIgnored?: boolean } = {
            jobId: job.id,
            status: job.status,
          };
          if (repoToken !== undefined) body.tokenIgnored = true;
          res.status(202).json(body);

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Send an OS-absolute path: /home/me/repos/foo on POSIX, C:\Users\me\repos\foo on Windows
  2. Resolve client-side first: path.resolve(input) or path.join(process.cwd(), input)
  3. Expand '~' with os.homedir() before sending — the server will not do it
  4. If you meant a remote repository, send "url" instead of "path"

Example fix

// before
const body = { path: inputDir }; // e.g. "../repos/foo" → 400

// after
import path from 'node:path';
const body = { path: path.resolve(inputDir) }; // "/home/me/repos/foo"
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';
// Normalize before sending
if (typeof body.path === 'string' && !path.isAbsolute(body.path)) {
  body.path = path.resolve(body.path);
}

Type guard

const isAcceptableAnalyzePath = (v: unknown): v is string =>
  typeof v === 'string' && path.isAbsolute(v);

Prevention

When it happens

Trigger: {"path": "../my-repo"}, {"path": "src"}, {"path": "~/repos/foo"} (tilde is never expanded and is not absolute), or any client forwarding cwd-relative input such as raw shell arguments or process.argv values.

Common situations: CLI-style users passing relative arguments; front-ends forwarding relative paths from config files; '~' home shorthand on POSIX; Windows drive-relative forms like "repo\sub" without a drive letter.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/6d98f9081465eb6c. Report an issue: GitHub.