OpenNHP/opennhp · error
no server cluster: set Cluster in resource.toml or use…
Error message
no server cluster: set Cluster in resource.toml or use --server
What it means
runRegisterApp resolves which server cluster should handle registration for the given asp-id. If the resolved Resource has neither Cluster nor ServerPubKey set, there is no routing target for the knock, so the command prints a hint and returns this error.
Solutions
- Add/fix the [[Resources]] entry in resource.toml so its AuthServiceProviderId matches the entered asp-id and set its Cluster.
- Pass --server <cluster-name> on the register command to target a cluster defined in server.toml explicitly.
- Verify the cluster name matches a [[Servers]] Name in server.toml.
- Confirm the agent is reading the resource.toml you edited (check the config directory it loads).
Example fix
// before (resource.toml) [[Resources]] AuthServiceProviderId = "my-app" # no Cluster // after [[Resources]] AuthServiceProviderId = "my-app" Cluster = "nhp-server-cluster"
Defensive patterns
Strategy: validation
Validate before calling
res := findResourceByAspId(aspId)
if res == nil || (res.Cluster == "" && res.ServerPubKey == "") {
return fmt.Errorf("no server cluster for asp-id %q; set Cluster in resource.toml or pass --server", aspId)
} Try / catch
sc, err := a.FindServerClusterFromResource(res)
if err != nil {
return fmt.Errorf("resolve cluster: %w", err)
} Prevention
- Give every [[Resources]] entry a Cluster matching a [[Servers]] Name in server.toml.
- Deploy resource.toml alongside agent config before registering.
- Use --server when resource.toml is incomplete or ambiguous.
When it happens
Trigger: runRegisterApp with res.Cluster == "" && res.ServerPubKey == "" — no [[Resources]] entry in resource.toml matches the entered asp-id, and no --server <cluster-name> flag was passed.
Common situations: Typo in asp-id so it matches no resource entry; resource.toml entry lacking a Cluster field; running registration on a machine whose resource.toml was never deployed; forgetting --server when multiple clusters exist.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/9d3477110aab6b26.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/agent/main/main.go:577
if serverCluster != "" {
res.Cluster = serverCluster
} else {
kt := a.FindKnockTarget(aspId, resId)
if kt == nil {
kt = a.FindKnockTargetByAspId(aspId)
}
if kt != nil {
sc := kt.GetServerCluster()
if sc != nil {
res.Cluster = sc.Name
}
}
}
if res.Cluster == "" && res.ServerPubKey == "" {
fmt.Printf(" %s❌ No server cluster found for asp-id=%q%s\n", colorYellow, aspId, colorReset)
fmt.Printf(" %sHint: configure a matching [[Resources]] entry in resource.toml or pass --server <cluster-name>%s\n\n", colorDim, colorReset)
return fmt.Errorf("no server cluster: set Cluster in resource.toml or use --server")
}
sc, err := a.FindServerClusterFromResource(res)
if err != nil {
fmt.Printf(" %s❌ Cannot resolve server cluster %q: %v%s\n\n", colorYellow, res.Cluster, err, colorReset)
fmt.Printf(" %sHint: ensure the cluster name matches a [[Servers]] Name in server.toml%s\n\n", colorDim, colorReset)
return err
}
target := &agent.KnockTarget{
KnockResource: *res,
ServerPeer: sc.RepresentativePeer(),
ServerCluster: sc,
}
// Step 1: OTP request (skipped when --otp is provided).
if otpCode == "" {
fmt.Printf(" %sStep 1/2:%s Requesting OTP from server...\n", colorBlue, colorReset)View on GitHub (pinned to 6e04ca5ff0)