hashicorp/nomad · error

root key ID is required

Error message

root key ID is required

What it means

The keyring Get endpoint requires a KeyID to look up. Since Get is a point-read of one root key, an empty KeyID cannot identify any key and is rejected up front after forwarding, before the blocking query is set up.

Solutions

  1. Populate args.KeyID with the root key ID before calling Get.
  2. List keys first (keyring list endpoint) to obtain a valid KeyID.
  3. Add a client-side check that KeyID is non-empty before issuing the RPC.

Example fix

// before
reply, err := client.Keyring().Get(&structs.KeyringGetRootKeyRequest{})
// after
if keyID == "" { return fmt.Errorf("keyID must be set") }
reply, err := client.Keyring().Get(&structs.KeyringGetRootKeyRequest{KeyID: keyID})
Defensive patterns

Strategy: validation

Validate before calling

if keyID == "" {
    return fmt.Errorf("cannot Get keyring key: keyID is empty")
}

Type guard

func keyIDProvided(req *structs.KeyringGetRootKeyRequest) bool {
    return req != nil && req.KeyID != ""
}

Try / catch

reply, err := client.Keyring().Get(req, nil)
if err != nil && strings.Contains(err.Error(), "root key ID is required") {
    return fmt.Errorf("caller bug: KeyID was empty; check %q source variable", idVar)
}

Prevention

When it happens

Trigger: Calling the Keyring.Get RPC (or GET /v1/kms/keys/<id>) with args.KeyID empty string — e.g. omitting the key ID in the URL path or passing an unset variable.

Common situations: A shell variable holding the key ID is empty/unset in a script; a UI or automation passes a nil-tenant placeholder; a wrapper function's argument is dropped.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/6a3d9233c35b181b. Report an issue: GitHub.

Appendix: source

Thrown at nomad/keyring_endpoint.go:277

}

// Get retrieves an existing key from the keyring, including both the
// key material and metadata. It is used only for replication.
func (k *Keyring) Get(args *structs.KeyringGetRootKeyRequest, reply *structs.KeyringGetRootKeyResponse) error {
	aclObj, err := k.srv.AuthenticateServerOnly(k.ctx, args)
	k.srv.MeasureRPCRate("keyring", structs.RateMetricRead, args)

	if err != nil || !aclObj.AllowServerOp() {
		return structs.ErrPermissionDenied
	}

	if done, err := k.srv.forward("Keyring.Get", args, args, reply); done {
		return err
	}
	defer metrics.MeasureSince([]string{"nomad", "keyring", "get"}, time.Now())

	if args.KeyID == "" {
		return fmt.Errorf("root key ID is required")
	}

	// Setup the blocking query
	opts := blockingOptions{
		queryOpts: &args.QueryOptions,
		queryMeta: &reply.QueryMeta,
		run: func(ws memdb.WatchSet, s *state.StateStore) error {

			snap, err := k.srv.fsm.State().Snapshot()
			if err != nil {
				return err
			}
			wrappedKey, err := snap.RootKeyByID(ws, args.KeyID)
			if err != nil {
				return err
			}
			if wrappedKey == nil {
				return k.srv.replySetIndex(state.TableRootKeys, &reply.QueryMeta)

View on GitHub (pinned to 482b49bf1a)