Summary
In artifacts/pve-specs/pve-api.json (and the YAML twin), every nested path key under paths is malformed: the full path-so-far is re-concatenated at every nesting level, instead of emitting
just the new segment. Top-level paths (/version, /nodes, /storage, /access, /cluster) are correct. Anything below them is broken.
The result is that any client driven by this spec calls bogus URLs and gets 404 from a real Proxmox VE node, even though the spec itself is OpenAPI-valid (parses fine, refs resolve, etc.).
Reproduction
Fetch the JSON spec and grep the paths object — many keys have repeated prefix chunks. Examples below taken verbatim from the current main branch.
| Spec key (broken) |
Actual PVE endpoint |
nodes/nodes/{node}/nodes/{node}/version |
nodes/{node}/version |
cluster/cluster/config/cluster/config/apiversion |
cluster/config/apiversion |
storage/storage/{storage} |
storage/{storage} |
access/access/users/access/users/{userid}/access/users/{userid}/token/access/users/{userid}/token/{tokenid} |
access/users/{userid}/token/{tokenid} |
nodes/nodes/{node}/nodes/{node}/qemu/nodes/{node}/qemu/{vmid}/nodes/{node}/qemu/{vmid}/status/nodes/{node}/qemu/{vmid}/status/start |
nodes/{node}/qemu/{vmid}/status/start |
nodes/nodes/{node}/nodes/{node}/storage/nodes/{node}/storage/{storage}/nodes/{node}/storage/{storage}/content |
nodes/{node}/storage/{storage}/content |
End-to-end test against a real PVE 9.1.9 node:
GET /api2/json/version → 200 OK (top-level path is correct in the spec)
GET /api2/json/nodes/<node>/version → 200 OK (the real endpoint exists)
GET /api2/json/nodes/nodes/<node>/nodes/<node>/version → would be 404 (the path the spec advertises does not exist on PVE)
Tested via a generic OpenAPI-driven HTTP client (rest-api-mcp) that loads the JSON spec, parses paths, and uses the keys verbatim as request paths. Top-level paths work; everything nested fails.
Pattern
The malformed key is the concatenation of every prefix from root to leaf, separated by /. Concretely, for the leaf endpoint nodes/{node}/qemu/{vmid}/status/start the spec emits:
nodes
+ / + nodes/{node}
+ / + nodes/{node}/qemu
+ / + nodes/{node}/qemu/{vmid}
+ / + nodes/{node}/qemu/{vmid}/status
+ / + nodes/{node}/qemu/{vmid}/status/start
i.e. nodes/nodes/{node}/nodes/{node}/qemu/nodes/{node}/qemu/{vmid}/nodes/{node}/qemu/{vmid}/status/nodes/{node}/qemu/{vmid}/status/start.
The canonical path is always the suffix starting at the last occurrence of the root segment. This holds uniformly across the sample I scanned (qemu, lxc, storage, access, cluster, ceph,
services, tasks, firewall, file-restore).
Likely cause
In the recursive walk that emits path keys (probably in scripts/unified_parser.py or one of the pve/ helpers), the recursion appears to pass the full path-so-far both as the key being
emitted and as the prefix for the next recursion level — so each child level receives parent_path + child_segment and emits parent_path + child_segment again, getting concatenated with the
next level. The fix is to emit only current_segment at each level (or equivalently, pass the segment list down and join once at the leaf).
Workaround for downstream users
While waiting for a fix, the spec can be salvaged client-side with a small post-processing step. For each key in paths:
def clean(p: str) -> str:
segs = p.split("/")
last = max(i for i, s in enumerate(segs) if s == segs[0])
return "/".join(segs[last:])
This is idempotent on already-correct top-level keys and reconstructs the canonical path for every malformed nested key in the current spec.
Impact
The spec is currently unusable as a contract for any code-generated client or schema-driven tool against a real PVE node — only the handful of top-level endpoints work. Anyone consuming
pve-api.json / pve-api.yaml directly will hit 404 on essentially the entire API surface.
Environment
- Spec source:
https://raw.githubusercontent.com/basher83/Proxmox-OpenAPI/refs/heads/main/artifacts/pve-specs/pve-api.json (current main)
- Target node: Proxmox VE 9.1.9 (
{"repoid":"ee7bad0a3d1546c9","version":"9.1.9","release":"9.1"})
- Auth used during reproduction: PVEAPIToken (no impact — auth path works, the URL path is wrong)
Summary
In
artifacts/pve-specs/pve-api.json(and the YAML twin), every nested path key underpathsis malformed: the full path-so-far is re-concatenated at every nesting level, instead of emittingjust the new segment. Top-level paths (
/version,/nodes,/storage,/access,/cluster) are correct. Anything below them is broken.The result is that any client driven by this spec calls bogus URLs and gets 404 from a real Proxmox VE node, even though the spec itself is OpenAPI-valid (parses fine, refs resolve, etc.).
Reproduction
Fetch the JSON spec and grep the
pathsobject — many keys have repeated prefix chunks. Examples below taken verbatim from the currentmainbranch.nodes/nodes/{node}/nodes/{node}/versionnodes/{node}/versioncluster/cluster/config/cluster/config/apiversioncluster/config/apiversionstorage/storage/{storage}storage/{storage}access/access/users/access/users/{userid}/access/users/{userid}/token/access/users/{userid}/token/{tokenid}access/users/{userid}/token/{tokenid}nodes/nodes/{node}/nodes/{node}/qemu/nodes/{node}/qemu/{vmid}/nodes/{node}/qemu/{vmid}/status/nodes/{node}/qemu/{vmid}/status/startnodes/{node}/qemu/{vmid}/status/startnodes/nodes/{node}/nodes/{node}/storage/nodes/{node}/storage/{storage}/nodes/{node}/storage/{storage}/contentnodes/{node}/storage/{storage}/contentEnd-to-end test against a real PVE 9.1.9 node:
GET /api2/json/version→ 200 OK (top-level path is correct in the spec)GET /api2/json/nodes/<node>/version→ 200 OK (the real endpoint exists)GET /api2/json/nodes/nodes/<node>/nodes/<node>/version→ would be 404 (the path the spec advertises does not exist on PVE)Tested via a generic OpenAPI-driven HTTP client (rest-api-mcp) that loads the JSON spec, parses
paths, and uses the keys verbatim as request paths. Top-level paths work; everything nested fails.Pattern
The malformed key is the concatenation of every prefix from root to leaf, separated by
/. Concretely, for the leaf endpointnodes/{node}/qemu/{vmid}/status/startthe spec emits:i.e.
nodes/nodes/{node}/nodes/{node}/qemu/nodes/{node}/qemu/{vmid}/nodes/{node}/qemu/{vmid}/status/nodes/{node}/qemu/{vmid}/status/start.The canonical path is always the suffix starting at the last occurrence of the root segment. This holds uniformly across the sample I scanned (qemu, lxc, storage, access, cluster, ceph,
services, tasks, firewall, file-restore).
Likely cause
In the recursive walk that emits path keys (probably in
scripts/unified_parser.pyor one of thepve/helpers), the recursion appears to pass the full path-so-far both as the key beingemitted and as the prefix for the next recursion level — so each child level receives
parent_path + child_segmentand emitsparent_path + child_segmentagain, getting concatenated with thenext level. The fix is to emit only
current_segmentat each level (or equivalently, pass the segment list down and join once at the leaf).Workaround for downstream users
While waiting for a fix, the spec can be salvaged client-side with a small post-processing step. For each key in
paths:This is idempotent on already-correct top-level keys and reconstructs the canonical path for every malformed nested key in the current spec.
Impact
The spec is currently unusable as a contract for any code-generated client or schema-driven tool against a real PVE node — only the handful of top-level endpoints work. Anyone consuming
pve-api.json/pve-api.yamldirectly will hit 404 on essentially the entire API surface.Environment
https://raw.githubusercontent.com/basher83/Proxmox-OpenAPI/refs/heads/main/artifacts/pve-specs/pve-api.json(currentmain){"repoid":"ee7bad0a3d1546c9","version":"9.1.9","release":"9.1"})