k3s-io/k3s · warning

unknown stage %s requested

Error message

unknown stage %s requested

What it means

The secrets-encrypt API dispatches on the 'stage' field of the request and only accepts prepare, rotate, rotate-keys, and re-encrypt-active. Any other stage string falls through the switch and is rejected with this message as HTTP 400 (error id 'secret-encrypt'). It is purely request-validation, not a cluster state problem.

Source

Thrown at pkg/server/handlers/secrets-encrypt.go:223

		}

		encryptReq, err := getEncryptionRequest(req)
		if err != nil {
			util.SendError(err, resp, req, http.StatusBadRequest)
			return
		}
		if encryptReq.Stage != nil {
			switch *encryptReq.Stage {
			case secretsencrypt.EncryptionPrepare:
				err = encryptionPrepare(ctx, control, encryptReq.Force)
			case secretsencrypt.EncryptionRotate:
				err = encryptionRotate(ctx, control, encryptReq.Force)
			case secretsencrypt.EncryptionRotateKeys:
				err = encryptionRotateKeys(ctx, control)
			case secretsencrypt.EncryptionReencryptActive:
				err = encryptionReencrypt(ctx, control, encryptReq.Force, encryptReq.Skip)
			default:
				err = fmt.Errorf("unknown stage %s requested", *encryptReq.Stage)
			}
		} else if encryptReq.Enable != nil {
			err = encryptionEnable(ctx, control, *encryptReq.Enable)
		}

		if err != nil {
			util.SendErrorWithID(err, "secret-encrypt", resp, req, http.StatusBadRequest)
			return
		}
		// If a user kills the k3s server immediately after this call, we run into issues where the files
		// have not yet been written. This sleep ensures that things have time to sync to disk before
		// the request completes.
		time.Sleep(1 * time.Second)
		resp.WriteHeader(http.StatusOK)
	})
}

func encryptionPrepare(ctx context.Context, control *config.Control, force bool) error {

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Use one of the four exact stage tokens: prepare, rotate, rotate-keys, re-encrypt-active.
  2. Check version skew: ensure the k3s CLI and server are the same release so stage vocabularies match.
  3. Trim/validate the stage string in your automation before sending (no whitespace, exact case).
  4. For enable/disable semantics, omit stage and set the enable boolean instead.

Example fix

# before
curl -sk -X POST $SERVER/v1-k3s/secrets-encrypt -d '{"stage":"reencrypt"}'

# after
curl -sk -X POST $SERVER/v1-k3s/secrets-encrypt -d '{"stage":"re-encrypt-active"}'
Defensive patterns

Strategy: validation

Validate before calling

var validStages = map[string]bool{"prepare":true,"rotate":true,"rotate-keys":true,"re-encrypt-active":true}
if req.Stage != nil && !validStages[*req.Stage] {
    http.Error(w, "stage must be one of prepare|rotate|rotate-keys|re-encrypt-active", 400)
}

Type guard

func isValidStage(s string) bool {
    switch s {
    case "prepare", "rotate", "rotate-keys", "re-encrypt-active":
        return true
    }
    return false
}

Prevention

When it happens

Trigger: POST to the secrets-encrypt handler with stage set to a typo or unsupported value: 'prepared', 'rotate-keys ' (whitespace), 'reencrypt', 're-encrypt', or an internal stage name from a different k3s version. Usually reached by calling the HTTP API directly or via a mismatched k3s CLI talking to a newer/older server.

Common situations: Custom automation hitting the endpoint with hand-built JSON; CLI/server version skew where the CLI sends a stage name the server build does not know; copy-paste stage names from documentation of another version.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/222a92e599023867. Report an issue: GitHub.