amruthpillai/reactive-resume · error · Error

Invalid storage key

Error message

Invalid storage key

What it means

LocalStorageService.resolvePath throws when, after stripping leading slashes and filtering out empty/. /.. segments, no segments remain. This means the key resolves to the storage root itself and is rejected to prevent operating on the root directory. Generic Error (surfaces as 500).

Source

Thrown at packages/api/src/features/storage/service.ts:220

				message: "Local filesystem storage is accessible and has read/write permission.",
			};
		} catch (error: unknown) {
			return {
				type: "local",
				status: "unhealthy",
				message: "Local filesystem storage is not accessible or lacks sufficient permissions.",
				error: error instanceof Error ? error.message : "Unknown error",
			};
		}
	}

	private resolvePath(key: string): string {
		const normalizedKey = key.replace(/^\/*/, "");
		const segments = normalizedKey
			.split(/[/\\]+/)
			.filter((segment) => segment.length > 0 && segment !== "." && segment !== "..");

		if (segments.length === 0) throw new Error("Invalid storage key");

		return join(this.rootDirectory, ...segments);
	}
}

class S3StorageService implements StorageService {
	private readonly bucket: string;
	private readonly client: S3Client;

	constructor() {
		if (!env.S3_ACCESS_KEY_ID || !env.S3_SECRET_ACCESS_KEY || !env.S3_BUCKET) {
			throw new Error("S3 credentials are not set");
		}

		this.bucket = env.S3_BUCKET;
		this.client = new S3Client({
			region: env.S3_REGION,
			forcePathStyle: env.S3_FORCE_PATH_STYLE,

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Validate the key is non-empty and contains at least one real path segment before calling storage methods.
  2. Trace where the blank key originated — typically a missing field on the upload metadata or a default-empty config value.
  3. Add a unit test on resolvePath for '', '/', '.', '..', '..' to lock in the contract.
  4. If a legitimate operation targets a directory prefix (list), use list(prefix) which accepts a prefix, not write/read/delete with an empty key.

Example fix

// before: passing an unvalidated key
await storage.write({ key: maybeBlank, data, contentType });
// after: guard the key
function safeKey(key: string): string {
  const cleaned = key.replace(/^\/+/, '').split(/[/\\]+/).filter(s => s && s !== '.' && s !== '..').join('/');
  if (!cleaned) throw new TypeError('storage key must have at least one path segment');
  return cleaned;
}
Defensive patterns

Strategy: validation

Validate before calling

function assertStorageKey(key: string): string {
  const segs = key.replace(/^\/+/, '').split(/[/\\]+/).filter(s => s && s !== '.' && s !== '..');
  if (segs.length === 0) throw new TypeError('storage key must contain at least one segment');
  return segs.join('/');
}

Type guard

function isValidStorageKey(key: string): key is string {
  return assertStorageKey.length > 0 && /[^./\\]/.test(key.replace(/^[./\\]+/, ''));
}

Prevention

When it happens

Trigger: Passing an empty string key; a key of only slashes ('///'); a key composed solely of '.'/'..' segments; a key that normalized to nothing (e.g. all segments filtered).

Common situations: Bug in caller that builds the key from an undefined/blank variable; a code path that passes a prefix instead of a full key; an upstream normalize step that collapsed the key to empty.

Related errors


AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12). Data as JSON: /api/errors/2058c5b7c86575b5. Report an issue: GitHub.