vercel/turborepo · error · Error
Invalid : path must be an absolute, non-empty string…
Error message
Invalid ${description}: path must be an absolute, non-empty string without NUL or newline characters and cannot start with "-" What it means
A defensive guard (assertSafeGitArgument) in @turbo-utils/examples.ts that validates any path handed to a git subprocess. It rejects values that are empty, non-strings, contain NUL or newline bytes, start with '-', or are not absolute filesystem paths. This blocks argument injection (e.g. '--upload-pack=...') and option smuggling into git commands.
Solutions
- Pass an absolute path: wrap the root with path.resolve(root) before calling the API
- If the path comes from user input, validate/normalize it before use (reject NUL/newlines/'-' prefixes)
- Ensure you are not passing an empty string for the root directory
- Check for stray control characters if the path was assembled from env vars or config files
Example fix
// before
import { downloadAndExtractExample } from "@turbo-utils/examples";
await downloadAndExtractExample(".", "basic"); // throws: relative path
// after
import { resolve } from "node:path";
import { downloadAndExtractExample } from "@turbo-utils/examples";
await downloadAndExtractExample(resolve("."), "basic"); Defensive patterns
Strategy: validation
Validate before calling
import { isAbsolute, resolve } from "node:path";
function toSafeGitPath(root: string): string {
const abs = isAbsolute(root) ? root : resolve(root);
if (abs.includes("\0") || abs.includes("\n") || abs.startsWith("-")) {
throw new Error(`Unsafe root path: ${JSON.stringify(root)}`);
}
return abs;
}
// await downloadAndExtractExample(toSafeGitPath(root), name); Type guard
function isSafeGitArgument(value: string): boolean {
return (
value.length > 0 &&
isAbsolute(value) &&
!value.includes("\0") &&
!value.includes("\n") &&
!value.startsWith("-")
);
} Prevention
- Always path.resolve() user-supplied destinations before passing them to create/scaffold APIs
- Sanitize any path that crosses into a subprocess command line
- Treat relative roots as invalid at your own boundary instead of relying on the library guard
When it happens
Trigger: Calling downloadAndExtractExample(root, name) with a relative root (like '.' or 'my-app'), an empty root, or a root string containing \0 or \n or leading with '-'. The resolved project root and the derived .turbo-clone-temp directory are both validated before runGit executes.
Common situations: Scripts calling createProject/downloadAndExtractExample with process.cwd() assumed absolute (it can be relative via execa cwd), Windows paths that were not resolved, user-supplied install targets taken straight from argv, or malicious/odd input passed as a destination.
Related errors
- Directory path contains potentially unsafe characters
- Invalid example name
- Blocked symlink
- `git ` failed
- Git download failed: Falling back to downloading the…
AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17).
Data as JSON: /api/errors/398806495de86eb2.
Report an issue: GitHub.
Appendix: source
Thrown at packages/turbo-utils/src/examples.ts:493
/**
* Validates that a path is safe to use as a git CLI argument.
* Prevents argument injection by rejecting paths that:
* - Are empty or not a string
* - Contain NUL bytes (could truncate the argument)
* - Start with "-" (could be interpreted as a git option)
* - Are not absolute filesystem paths (helps ensure they cannot be mistaken
* for URLs or additional git options like "--upload-pack")
*/
function assertSafeGitArgument(value: string, description: string): void {
if (
!value ||
typeof value !== "string" ||
value.includes("\0") ||
value.includes("\n") ||
value.startsWith("-") ||
!isAbsolute(value)
) {
throw new Error(
`Invalid ${description}: path must be an absolute, non-empty string without NUL or newline characters and cannot start with "-"`
);
}
}
function formatError(error: unknown): string {
if (error && typeof error === "object" && "stderr" in error) {
const { stderr } = error as { stderr?: unknown };
if (typeof stderr === "string" || Buffer.isBuffer(stderr)) {
const output = stderr.toString().trim();
if (output) {
return output;
}
}
}
return error instanceof Error ? error.message : String(error);
}View on GitHub (pinned to f9245100cf)