```ts
/**
 * Finds all leaf paths in a hierarchy described by rows of [nodeId, parentId].
 * Returns paths from root to each leaf, sorted lexicographically.
 * Throws errors for invalid inputs.
 */
export function leafPaths(rows: [string, string][]): string[] {
  if (rows.length === 0) {
    throw new Error("Input cannot be empty");
  }

  const idToParent = new Map<string, string>();
  const parentToChildren = new Map<string, Set<string>>();
  const roots: string[] = [];

  for (const [nodeId, parentId] of rows) {
    if (idToParent.has(nodeId)) {
      throw new Error("Duplicated node id");
    }
    idToParent.set(nodeId, parentId);

    if (!parentToChildren.has(parentId)) {
      parentToChildren.set(parentId, new Set());
    }
    parentToChildren.get(parentId)!.add(nodeId);

    if (parentId === "") {
      roots.push(nodeId);
    }
  }

  if (roots.length !== 1) {
    throw new Error("Input must have exactly one root row");
  }

  const leafPaths: string[] = [];

  function dfs(nodeId: string, path: string): void {
    const children = parentToChildren.get(nodeId);
    if (!children || children.size === 0) {
      leafPaths.push(path);
      return;
    }
    for (const child of children) {
      dfs(child, `${path}/${child}`);
    }
  }

  dfs(roots[0], roots[0]);

  return leafPaths.sort();
}
```