getsops/sops · error

failed to assume role '%s': %w

Error message

failed to assume role '%s': %w

What it means

createSTSConfig performs sts.AssumeRole so the KMS key is accessed with the temporary credentials of key.Role. When the AssumeRole API call fails, sops wraps the SDK error with the role name. Typical inner causes are: role does not exist, caller is not authorized to assume it (sts:AssumeRole denied), trust policy mismatch, or expired/insufficient credentials.

Source

Thrown at kms/keysource.go:450

}

// createSTSConfig uses AWS STS to assume a role and returns a config
// configured with that role's credentials. It returns an error if
// it fails to construct a session name, or assume the role.
func (key MasterKey) createSTSConfig(ctx context.Context, config *aws.Config) (*aws.Config, error) {
	name, err := stsSessionName()
	if err != nil {
		return nil, err
	}
	input := &sts.AssumeRoleInput{
		RoleArn:         &key.Role,
		RoleSessionName: &name,
	}

	client := sts.NewFromConfig(*config)
	out, err := client.AssumeRole(ctx, input)
	if err != nil {
		return nil, fmt.Errorf("failed to assume role '%s': %w", key.Role, err)
	}

	config.Credentials = credentials.NewStaticCredentialsProvider(*out.Credentials.AccessKeyId,
		*out.Credentials.SecretAccessKey, *out.Credentials.SessionToken,
	)
	return config, nil
}

// stsSessionName returns the name for the STS session in the format of
// `sops@<hostname>`. It sanitizes the hostname with stsSessionRegex, and
// truncates to roleSessionNameLengthLimit when it exceeds the limit.
func stsSessionName() (string, error) {
	hostname, err := osHostname()
	if err != nil {
		return "", fmt.Errorf("failed to construct STS session name: %w", err)
	}

	re := regexp.MustCompile(stsSessionRegex)

View on GitHub (pinned to 13442bb981)

Solutions

  1. Inspect the wrapped SDK error: AccessDenied means fix IAM (attach sts:AssumeRole to the caller and matching trust policy); NotFound means fix the role ARN.
  2. Verify the role exists: `aws sts get-caller-identity` then `aws iam get-role --role-name <name>` in the right account/region.
  3. Test manually with `aws sts assume-role --role-arn <arn> --role-session-name test` to confirm credentials allow it.
  4. Correct the `role` field on the KMS key in .sops.yaml or remove it if not needed (then no AssumeRole is attempted).

Example fix

// before (.sops.yaml)
- arn: arn:aws:kms:us-east-1:123456789012:key/abcd
  role: "arn:aws:iam::123456789012:role/sops-role"  # typo: 'iam' vs 'sts' context / wrong path
// after
- arn: arn:aws:kms:us-east-1:123456789012:key/abcd
  role: "arn:aws:iam::123456789012:role/SOPSKeyRole"  # verified via aws iam get-role
Defensive patterns

Strategy: retry

Validate before calling

// Go: dry-run AssumeRole before encrypting
_, err := sts.NewFromConfig(cfg).AssumeRole(ctx, &sts.AssumeRoleInput{
  RoleArn: aws.String(roleArn), RoleSessionName: aws.String("sops-preflight")})
// surface the error early instead of mid-encryption

Try / catch

// Go
_, err := key.Encrypt()
var opErr *types.UnrecognizedClientException // and AccessDenied flows
if err != nil && strings.Contains(err.Error(), "failed to assume role") {
  if strings.Contains(err.Error(), "AccessDenied") { /* fix IAM, do not retry */ }
  // transient errors only: backoff and retry
}

Prevention

When it happens

Trigger: EncryptContext/DecryptContext on a KMS MasterKey with a non-empty Role field where client.AssumeRole returns an AccessDenied, NoSuchEntity/NotFound, or credentials error.

Common situations: Typo in the role ARN; the calling identity lacks sts:AssumeRole permission; the role's trust policy does not trust the caller; SCP or permission boundary denies STS; using the role from an account without proper external-id conditions; MFA-required trust policy without MFA parameters.

Related errors


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/c074d42559e431e7. Report an issue: GitHub.