hashicorp/nomad · error
Unknown port label %q
Error message
Unknown port label %q
What it means
When a port_map is given, StartTask maps each entry (label -> guest port) to a host port taken from the first declared network resource. If the task's network block declares no port with the given label, PortLabels() lookup fails and StartTask returns this error instead of building the hostfwd args. Every port_map key must correspond to a static or dynamic port label in the task's network resources.
Source
Thrown at drivers/qemu/driver.go:603
// For example, args = [ "-nodefconfig", "-nodefaults" ]
// This will allow a VM with embedded configuration to boot successfully.
args = append(args, driverConfig.Args...)
// Check the Resources required Networks to add port mappings. If no resources
// are required, we assume the VM is a purely compute job and does not require
// the outside world to be able to reach it. VMs ran without port mappings can
// still reach out to the world, but without port mappings it is effectively
// firewalled
protocols := []string{"udp", "tcp"}
if len(cfg.Resources.NomadResources.Networks) > 0 {
// Loop through the port map and construct the hostfwd string, to map
// reserved ports to the ports listenting in the VM
// Ex: hostfwd=tcp::22000-:22,hostfwd=tcp::80-:8080
taskPorts := cfg.Resources.NomadResources.Networks[0].PortLabels()
for label, guest := range driverConfig.PortMap {
host, ok := taskPorts[label]
if !ok {
return nil, nil, fmt.Errorf("Unknown port label %q", label)
}
for _, p := range protocols {
netdevArgs = append(netdevArgs, fmt.Sprintf("hostfwd=%s::%d-:%d", p, host, guest))
}
}
if len(netdevArgs) != 0 {
args = append(args,
"-netdev",
fmt.Sprintf("user,id=user.0,%s", strings.Join(netdevArgs, ",")),
"-device", "virtio-net,netdev=user.0",
)
}
}
// If using KVM, add optimization args
if accelerator == "kvm" {View on GitHub (pinned to 482b49bf1a)
Solutions
- Declare a matching port in the task's network block for every port_map key: network { port "http" {} } plus port_map { http = 80 }.
- Make label strings in port_map and the network stanza match exactly (case-sensitive).
- If multiple network blocks exist, ensure the needed labels are in the first one.
Example fix
// before
network {
port "web" {}
}
// port_map uses "http" -> Unknown port label
// after
network {
port "http" {}
}
// port_map { http = 80 } Defensive patterns
Strategy: validation
Validate before calling
function validatePortMap(task) {
const netLabels = new Set(
(task.resources?.networks || []).flatMap((n) => Object.keys(n.port_labels || n["port"] || {}))
);
for (const label of Object.keys(task.driver?.port_map || {})) {
if (!netLabels.has(label)) {
throw new Error(`Unknown port label ${label}: declare it in the network block`);
}
}
} Type guard
function portMapLabelsDeclared(task) {
const labels = new Set((task.resources?.networks?.[0]?.ports || []).map((p) => p.label));
return Object.keys(task.driver?.port_map || {}).every((l) => labels.has(l));
} Try / catch
try {
await nomad.jobs.register(job);
} catch (e) {
if (String(e.message).startsWith("Unknown port label")) {
const label = JSON.parse(JSON.stringify(e.message)).match(/"(.*)"/)?.[1];
console.error(`Add network port "${label}" to the task's network block`);
}
throw e;
} Prevention
- Keep port_map keys and network port labels in one template source so they cannot drift.
- Declare ports in the first network block — only Networks[0] is consulted.
- Label matching is case-sensitive; use consistent casing.
- Dry-run job parsing (nomad job validate) before submitting.
When it happens
Trigger: driver config port_map contains a label (e.g. "http") that is not declared as a port in the task's network stanza; network stanza omitted entirely (so NomadResources.Networks is empty/mislabeled); label typo between port_map and the network block.
Common situations: Task author renames a port label in the network block but forgets port_map; port_map written for a driver that does not require a network block; copy-pasted port_map from another task with different labels; multiple network blocks where only Networks[0] is consulted so labels live in a later block.
Related errors
- QEMU graceful shutdown is unsupported on the Windows platfor
- No path to region
- failed to initialize table forwarding rules: %v
- cannot use address_mode="alloc": no allocation network statu
- Connect only supported with exactly 1 network (found %d)
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/cbaed6d8923f9932.
Report an issue: GitHub.