kubernetes/kops · critical

%s is required

Error message

%s is required

What it means

NewLinodeVerifier builds a bootstrap.Verifier for Akamai (Linode) instance tokens and requires a Linode API access token. It reads LINODE_TOKEN from the environment; if the variable is unset or empty, construction fails immediately with '%s is required' where the placeholder is filled with LINODE_TOKEN. No Linode client can be created without credentials, so this is an intentional fail-fast guard.

Source

Thrown at upup/pkg/fi/cloudup/linode/verifier.go:50

)

type LinodeVerifierOptions struct{}

type linodeVerifierClient interface {
	GetInstance(ctx context.Context, linodeID int) (*linodego.Instance, error)
}

type linodeVerifier struct {
	client linodeVerifierClient
}

var _ bootstrap.Verifier = (*linodeVerifier)(nil)

// NewLinodeVerifier returns a bootstrap.Verifier that can verify Akamai (Linode) instance tokens using the LINODE_TOKEN environment variable.
func NewLinodeVerifier(opt *LinodeVerifierOptions) (bootstrap.Verifier, error) {
	accessToken := os.Getenv("LINODE_TOKEN")
	if accessToken == "" {
		return nil, fmt.Errorf("%s is required", "LINODE_TOKEN")
	}

	client, err := linodego.NewClient(nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create Linode client: %w", err)
	}
	client.SetUserAgent("kops")
	client.SetToken(accessToken)

	return &linodeVerifier{client: &client}, nil
}

// VerifyToken verifies that the given token corresponds to a valid Akamai (Linode) instance.
func (v *linodeVerifier) VerifyToken(ctx context.Context, rawRequest *http.Request, token string, body []byte) (*bootstrap.VerifyResult, error) {
	if !strings.HasPrefix(token, linodemetadata.LinodeAuthenticationTokenPrefix) {
		return nil, bootstrap.ErrNotThisVerifier
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Export LINODE_TOKEN with a valid Linode API access token in the environment of the process running the verifier
  2. If running under systemd or a container, add the env var to the unit file / container spec rather than the interactive shell
  3. Check the exact variable name is LINODE_TOKEN (no typos or alternate names)
  4. Verify with: echo "${LINODE_TOKEN:?not set}" before launching
  5. In tests, set t.Setenv("LINODE_TOKEN", "dummy") before calling NewLinodeVerifier

Example fix

// before (shell)
kops executor verify --v=2
// after (shell)
export LINODE_TOKEN="<linode-api-access-token>"
kops executor verify --v=2
Defensive patterns

Strategy: validation

Validate before calling

if os.Getenv("LINODE_TOKEN") == "" {
	return errors.New("LINODE_TOKEN is required")
}

Try / catch

verifier, err := linode.NewLinodeVerifier(opts)
if err != nil {
	if strings.Contains(err.Error(), "LINODE_TOKEN is required") {
		return fmt.Errorf("set LINODE_TOKEN in the service environment: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: NewLinodeVerifier(opt) is called while os.Getenv("LINODE_TOKEN") returns "" — the variable is not exported in the verifier's environment, is set to an empty string, or is defined only in a shell profile not loaded by the kOps process (e.g. systemd service, container).

Common situations: Running the verifier/bastion on a node where credentials were never provisioned; exporting LINODE_TOKEN in an interactive shell but launching kops via systemd/k8s which has a different env; CI jobs missing the secret in their env config; typo such as LINODE_API_TOKEN instead of LINODE_TOKEN.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/be31152e5ef72879. Report an issue: GitHub.