OpenNHP/opennhp · error

key not found

Error message

key not found

What it means

EtcdConn.GetValue fetches the key configured on the connection from etcd. If the Get succeeds but etcd returns zero key-value records, the key simply does not exist in the cluster and this error is returned, aborting remote config loading in the AC/server.

Solutions

  1. Put the config value into etcd at the exact configured key (etcdctl put <key> '<value>')
  2. Compare conn.Key with the key actually present (etcdctl get <key> --prefix)
  3. Point the config at the correct etcd endpoints/environment where the key exists
  4. Re-run the seeding script/tool that writes remote config on deploy

Example fix

// before (empty cluster)
etcdctl get /opennhp/server/config  # empty
// after
etcdctl put /opennhp/server/config '{...}'
etcdctl get /opennhp/server/config
Defensive patterns

Strategy: retry

Validate before calling

// before starting daemons
resp, err := etcdCli.Get(ctx, key)
if err != nil { return err }
if resp.Count == 0 {
    return fmt.Errorf("seed etcd key %s before starting", key)
}

Try / catch

val, err := conn.GetValue()
if err != nil {
    if err.Error() == "key not found" {
        return fmt.Errorf("remote config key %q missing; run seeding script", conn.Key)
    }
    return err
}

Prevention

When it happens

Trigger: loadRemoteConfig (AC) or loadRemoteBaseConfig (server) calls GetValue while the etcd key was never written, was deleted, or the connection points at the wrong namespace/prefix/key name.

Common situations: Fresh etcd cluster not yet seeded with config; key deleted by TTL or compaction; config Key field pointing at a different path than where the operator stored the value; wrong etcd endpoints/environment selected.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/373864ea7476573c. Report an issue: GitHub.

Appendix: source

Thrown at nhp/etcd/etcdconn.go:63

	}
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	conn.ctx = ctx
	_, err = conn.client.Status(conn.ctx, conn.Endpoints[0])
	if err != nil {
		return err
	}
	return nil
}

func (conn *EtcdConn) GetValue() ([]byte, error) {
	val, err := conn.client.Get(conn.ctx, conn.Key)
	if err != nil {
		return nil, err
	}

	if len(val.Kvs) == 0 {
		return nil, errors.New("key not found")
	}
	if len(val.Kvs[0].Value) == 0 {
		return nil, errors.New("value not set")
	}
	return val.Kvs[0].Value, nil
}

func (conn *EtcdConn) SetValue(v string) error {
	_, err := conn.client.Put(conn.ctx, conn.Key, v)
	return err
}

func (conn *EtcdConn) WatchValue(callbackFunc func(val []byte)) {
	// create etcd watcher
	conn.watcher = clientv3.NewWatcher(conn.client)

	watchChan := conn.watcher.Watch(context.Background(), conn.Key)

View on GitHub (pinned to 6e04ca5ff0)