OpenNHP/opennhp · error

: [[Servers]][ ] missing PubKeyBase64

Error message

%s: [[Servers]][%d] missing PubKeyBase64

What it means

Normalize requires every [[Servers]] cluster entry to carry PubKeyBase64 — the server's base64 public key used to build peer tables and encrypt traffic. An entry without a key is unusable and rejected with its index.

Solutions

  1. Set PubKeyBase64 to the target server's base64 public key (from its keygen output)
  2. Ensure the templating/secret pipeline actually injects the key (check for empty env var / missing secret)
  3. Regenerate and redeploy the key pair if the server's public key changed
  4. Validate the TOML after rendering so empty key fields fail the deploy before Normalize does

Example fix

// before (server.toml)
[[Servers]]
Name = "nhp-server-1"
// after
[[Servers]]
Name = "nhp-server-1"
PubKeyBase64 = "<base64 public key>"
Defensive patterns

Strategy: validation

Validate before calling

for i, c := range clusters {
    if c.PubKeyBase64 == "" {
        return fmt.Errorf("cluster %d (%s) has empty PubKeyBase64 — check secret injection", i, c.Name)
    }
}
err := clusterconfig.Normalize(clusters, opts)

Try / catch

if err := clusterconfig.Normalize(clusters, opts); err != nil {
    if strings.Contains(err.Error(), "missing PubKeyBase64") {
        return fmt.Errorf("server public key missing — did generate-nhp-keys.sh run? %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Normalize with clusters[i].PubKeyBase64 == "", usually from a TOML [[Servers]] block that omits the PubKeyBase64 key or sets it to an empty string.

Common situations: Hand-written server.toml missing the key line; a template variable like ${SERVER_PUB_KEY} left unsubstituted/empty because the secret was absent; copying a cluster block as a template placeholder and not filling it in.

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/f9a7a2b3f7424e4a. Report an issue: GitHub.

Appendix: source

Thrown at nhp/common/clusterconfig/clusterconfig.go:147

// invocations for assertions. A nil deprecate is treated as no-op.
func Normalize(clusters []*ClusterConfig, opts Options, deprecate func(string, ...any)) error {
	if deprecate == nil {
		deprecate = func(string, ...any) {}
	}
	label := opts.ConsumerLabel
	if label == "" {
		label = "cluster"
	}

	if len(clusters) == 0 {
		return fmt.Errorf("%s: no [[Servers]] configured", label)
	}
	for i, c := range clusters {
		if c == nil {
			return fmt.Errorf("%s: [[Servers]][%d] is nil", label, i)
		}
		if c.PubKeyBase64 == "" {
			return fmt.Errorf("%s: [[Servers]][%d] missing PubKeyBase64", label, i)
		}
		if opts.RequireName {
			if c.Name == "" {
				return fmt.Errorf("%s: [[Servers]][%d] (%s) missing Name — clusters are referenced from resource.toml by Name",
					label, i, c.PubKeyBase64)
			}
			if len(c.Name) > NameMaxLen {
				return fmt.Errorf("%s: [[Servers]][%d] Name %q exceeds %d chars",
					label, i, c.Name, NameMaxLen)
			}
			if !clusterNameRegex.MatchString(c.Name) {
				return fmt.Errorf("%s: [[Servers]][%d] Name %q invalid — allowed chars: [a-zA-Z0-9._-]",
					label, i, c.Name)
			}
		}

		legacy := c.hasLegacyFields()
		hasInstances := len(c.Instances) > 0

View on GitHub (pinned to 6e04ca5ff0)