A cycle in the data — a category set as its own ancestor through the admin — recurses until cte_max_recursion_depth stops it with an error, and the default is a thousand.
WITH RECURSIVE tree AS (
SELECT id, parent_id, name, 0 AS depth
FROM categories WHERE id = 12
UNION ALL
SELECT c.id, c.parent_id, c.name, t.depth + 1
FROM categories c JOIN tree t ON c.parent_id = t.id
WHERE t.depth < 10
)
SELECT * FROM tree ORDER BY depth, name;
The explicit depth column turns a server error into a bounded result, which is the difference between a page that fails and a page that renders ten levels. An outer LIMIT does not help, because the recursion happens before the limit is applied. Worth adding a uniqueness check on the path as well for data that genuinely might contain a cycle — the depth guard bounds the damage and does not detect the problem.