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
- Use one of the four exact stage tokens: prepare, rotate, rotate-keys, re-encrypt-active.
- Check version skew: ensure the k3s CLI and server are the same release so stage vocabularies match.
- Trim/validate the stage string in your automation before sending (no whitespace, exact case).
- 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
- Use the k3s CLI rather than raw HTTP for stage requests
- Keep CLI and server versions in lockstep
- Treat HTTP 400 with id 'secret-encrypt' as a request bug, not cluster state
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
- token must not be empty
- invalid snapshot operation
- incorrect stage: %s found on node %s
- Failed checking netMode
- Initial server URL host is not defined for load balancer
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/222a92e599023867.
Report an issue: GitHub.