heygen-com/hyperframes · error · InvalidProjectError

Invalid project directory: #

Error message

Invalid project directory: #

What it means

resolveProjectOrThrow special-cases dirArg === '#' (after trim). '#' is a URL fragment, not a filesystem path — it appears when a user pastes a URL or a shell-quoting artifact where the project directory argument should go. The library throws InvalidProjectError with a targeted hint and suggestion rather than letting it fall through to the generic 'Not a directory' check, because the '#' case is a known copy-paste footgun.

Source

Thrown at packages/cli/src/utils/project.ts:37

  readonly hint?: string;
  readonly suggestion?: string;

  constructor(title: string, hint?: string, suggestion?: string) {
    super(title);
    this.name = "InvalidProjectError";
    this.title = title;
    this.hint = hint;
    this.suggestion = suggestion;
  }
}

export function resolveProjectOrThrow(
  dirArg: string | undefined,
  options: ResolveProjectOptions = {},
): ProjectDir {
  const trimmed = dirArg?.trim();
  if (trimmed === "#") {
    throw new InvalidProjectError(
      "Invalid project directory: #",
      "# is a URL fragment, not a project path.",
      "Run hyperframes preview . from your project directory.",
    );
  }

  const dir = resolve(dirArg ?? ".");
  const name = basename(dir);
  const indexPath = resolve(dir, "index.html");

  if (!existsSync(dir) || !statSync(dir).isDirectory()) {
    throw new InvalidProjectError("Not a directory: " + dir);
  }
  if (options.requireIndex !== false && !existsSync(indexPath)) {
    throw new InvalidProjectError(
      "No composition found in " + dir,
      "No index.html file found.",
      "Run npx hyperframes init to create a new composition.",

View on GitHub (pinned to c2996c8626)

Solutions

  1. Run from inside your project directory with `.` or no argument: `hyperframes preview .`.
  2. Pass the actual project path: `hyperframes preview /path/to/project`.
  3. If the '#' came from a URL, strip everything from '#' onwards before passing it as a path.
  4. Audit wrapper scripts for unquoted variable expansions that could yield '#'.

Example fix

# before
hyperframes preview #
# after
hyperframes preview .   # from inside the project directory
Defensive patterns

Strategy: validation

Validate before calling

function isLikelyValidProjectArg(v: string | undefined): boolean {
  const t = v?.trim();
  return t !== undefined && t !== '' && t !== '#';
}

if (!isLikelyValidProjectArg(process.argv[2])) {
  throw new Error('Pass a project directory (or `.`); got: ' + process.argv[2]);
}

Type guard

function isNonFragmentPath(v: unknown): v is string {
  return typeof v === 'string' && v.trim() !== '' && v.trim() !== '#';
}

Try / catch

try {
  return resolveProjectOrThrow(dirArg);
} catch (err) {
  if (err instanceof InvalidProjectError && /Invalid project directory: #/.test(err.message)) {
    console.error(err.hint, err.suggestion);
  } else throw err;
}

Prevention

When it happens

Trigger: Running a CLI command with '#' as the project argument: `hyperframes preview #`, or a shell-expanded variable that resolved to '#', or pasting a URL whose fragment leaked into argv. Also when a script passes through an unescaped anchor reference.

Common situations: User copies a URL like https://example.com/composition# and the trailing # lands as argv; a bash history substitution gone wrong (!# expansion); an empty arg that defaulted to '#' in a wrapper script; CI config that echoes a URL fragment into the project slot.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/14b2682608a57616. Report an issue: GitHub.