```ts
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, string[]>();

    for (const [id, parentId] of rows) {
        if (idToParent.has(id)) {
            throw new Error(`Duplicate id: ${id}`);
        }
        idToParent.set(id, parentId);

        if (!parentToChildren.has(parentId)) {
            parentToChildren.set(parentId, []);
        }
        parentToChildren.get(parentId)!.push(id);
    }

    const rootRows = rows.filter(([_, parentId]) => parentId === "");
    if (rootRows.length !== 1) {
        throw new Error("Input must have exactly one root row");
    }

    const rootNode = rootRows[0][0];
    const paths: string[] = [];

    function dfs(node: string, path: string) {
        if (!parentToChildren.has(node)) {
            paths.push(path);
            return;
        }
        for (const child of parentToChildren.get(node)!) {
            dfs(child, `${path}/${child}`);
        }
    }

    dfs(rootNode, rootNode);

    if (paths.length === 0) {
        throw new Error("No leaf nodes reachable from the root");
    }

    return paths.sort();
}
```