abhigyanpatwari/GitNexus · error · BadRequestError
Invalid upload path
Error message
Invalid upload path
What it means
resolveContainedDest is the load-bearing path-traversal control for browser folder uploads in gitnexus's serve upload-ingest pipeline. This first throw rejects a manifest webkitRelativePath that is not a non-empty string: a missing/undefined manifest entry, a number, null, or ''. Every file part's declared path passes through here before any filesystem write, so malformed values never reach disk.
Source
Thrown at gitnexus/src/server/upload-ingest.ts:68
export interface IngestResult {
/** Absolute path to the populated staging directory (realpath-canonical). */
stageRoot: string;
fileCount: number;
totalBytes: number;
/** First path segment shared by the uploaded tree (the picked folder). */
topLevelName: string;
}
/**
* Resolve a client-provided relative path to an absolute destination PROVABLY
* contained within `stageRoot`. Throws BadRequestError on any unsafe input.
* This is the load-bearing path-traversal-on-write control; keep it pure and
* unit-tested.
*/
export function resolveContainedDest(stageRoot: string, rel: unknown): string {
if (typeof rel !== 'string' || rel.length === 0) {
throw new BadRequestError('Invalid upload path');
}
if (rel.length > MAX_PATH_LENGTH) {
throw new BadRequestError('Upload path too long');
}
// webkitRelativePath is always relative; a leading slash is absolute/hostile.
if (rel.startsWith('/')) {
throw new BadRequestError('Invalid upload path');
}
// Browsers emit forward slashes only; a NUL byte or backslash is hostile.
if (rel.includes('\u0000') || rel.includes('\\')) {
throw new BadRequestError('Invalid upload path');
}
const rawSegments = rel.split('/').filter((s) => s.length > 0);
if (rawSegments.length === 0 || rawSegments.length > MAX_PATH_DEPTH) {
throw new BadRequestError('Invalid upload path');
}
const segments: string[] = [];
for (const seg of rawSegments) {View on GitHub (pinned to aac7515d2a)
Solutions
- In your uploader, always send file.webkitRelativePath || file.name for every file part so the manifest path is a non-empty string
- Validate the manifest client-side before POSTing: every entry must be a string of length > 0
- If you hit this as an API consumer, treat HTTP 400 with 'Invalid upload path' as a client bug in manifest construction, not a server fault
Example fix
// before — client manifest omits path for root files
manifest.files.forEach(f => entries.push({ path: f.webkitRelativePath })); // undefined for some picks
// after
manifest.files.forEach(f => entries.push({ path: f.webkitRelativePath || f.name })); Defensive patterns
Strategy: type-guard
Validate before calling
// Client-side: build a manifest that can never contain empty/non-string paths
const entries = files.map((f) => ({
path: (f.webkitRelativePath || f.name) as string, // always non-empty string
})); Type guard
function isNonEmptyRelativePath(rel: unknown): rel is string {
return (
typeof rel === 'string' &&
rel.length > 0 &&
!rel.startsWith('/') &&
!rel.includes('\\') &&
!rel.includes('\u0000')
);
} Prevention
- Always send file.webkitRelativePath || file.name for every file part
- Validate the manifest client-side (type, non-empty, forward slashes only) before POSTing
- Treat HTTP 400 'Invalid upload path' as a client bug — fix the uploader, don't retry the same body
When it happens
Trigger: POST multipart upload to the serve ingest endpoint where a file part's path in the JSON manifest is absent, '', null, a number, or any non-string; a hand-rolled uploader client that omits webkitRelativePath for root-level files instead of sending just the filename; manifest deserialization producing undefined for skipped keys.
Common situations: Custom upload clients not using the browser File.webkitRelativePath API (drag-drop scripts, curl-based tests); older browsers/polyfills where webkitRelativePath is undefined; a manifest built from Object.keys with a typo'd key so lookups yield undefined; API consumers testing the endpoint with fabricated manifests.
Related errors
- Upload path must not contain traversal segments
- Upload must be a single folder of files
- Upload must be a folder
- Upload path too long
- Upload path escapes the sandbox
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/351d112376333e04.
Report an issue: GitHub.