angular/angular-cli · error · Error

Invalid toPath: The string must start with a '/'. Received:

Error message

Invalid toPath: The string must start with a '/'. Received: '${toPath}'

What it means

buildPathWithParams in @angular/ssr merges route parameters from a source path into a destination ('to') path. Both arguments must be root-relative absolute paths. It throws when the destination path (toPath) does not start with '/', because relative destinations cannot be deterministically resolved against the base.

Source

Thrown at packages/angular/ssr/src/utils/url.ts:186

 * @returns A resolved path string with `*` placeholders replaced by segments from the `fromPath`,
 * or the `toPath` returned unchanged if it contains no placeholders.
 *
 * @throws If the `toPath` does not start with a `/`, indicating an invalid path format.
 *
 * @example
 * ```typescript
 * // Example with placeholders resolved
 * const resolvedPath = buildPathWithParams('/*\/details', '/123/abc');
 * console.log(resolvedPath); // Outputs: '/123/details'
 *
 * // Example with a static path
 * const staticPath = buildPathWithParams('/static/path', '/base/unused');
 * console.log(staticPath); // Outputs: '/static/path'
 * ```
 */
export function buildPathWithParams(toPath: string, fromPath: string): string {
  if (toPath[0] !== '/') {
    throw new Error(`Invalid toPath: The string must start with a '/'. Received: '${toPath}'`);
  }

  if (fromPath[0] !== '/') {
    throw new Error(`Invalid fromPath: The string must start with a '/'. Received: '${fromPath}'`);
  }

  if (!toPath.includes('/*')) {
    return toPath;
  }

  const fromPathParts = fromPath.split('/');
  const toPathParts = toPath.split('/');
  const resolvedParts = toPathParts.map((part, index) =>
    toPathParts[index] === '*' ? fromPathParts[index] : part,
  );

  return joinUrlParts(...resolvedParts);
}

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Prefix the destination with '/': buildPathWithParams('/' + toPath.replace(/^\/+/, ''), fromPath).
  2. Verify the value passed as toPath is the configured route path, not a relative file path or empty string.
  3. If paths come from user/config input, validate with toPath.startsWith('/') before calling.

Example fix

// before
buildPathWithParams(route.template, '/base');
// after
const toPath = route.template.startsWith('/') ? route.template : '/' + route.template;
buildPathWithParams(toPath, '/base');
Defensive patterns

Strategy: validation

Validate before calling

function isValidToPath(p: unknown): p is string {
  return typeof p === 'string' && p.startsWith('/');
}
if (!isValidToPath(toPath)) throw new TypeError(`toPath must be absolute: got '${toPath}'`);

Type guard

function isAbsolutePath(p: unknown): p is string {
  return typeof p === 'string' && p.startsWith('/');
}

Try / catch

try {
  const staticPath = buildPathWithParams(toPath, fromPath);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Invalid toPath:')) {
    // fall back to normalized path
  } else throw e;
}

Prevention

When it happens

Trigger: Calling buildPathWithParams('static/path', '/base') or any toPath lacking a leading slash (empty string, 'foo/bar', './foo') from handle/result flows.

Common situations: Joining path segments with string concatenation instead of a path utility so the leading slash is lost; stripping the slash with regex trims; reading a route path from config that was authored as relative; passing an empty string when a route is missing.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/8092cef0d8d3574f. Report an issue: GitHub.