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

  1. Add/fix the [[Resources]] entry in resource.toml so its AuthServiceProviderId matches the entered asp-id and set its Cluster.
  2. Pass --server <cluster-name> on the register command to target a cluster defined in server.toml explicitly.
  3. Verify the cluster name matches a [[Servers]] Name in server.toml.
  4. 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

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)