hashicorp/terraform · error

sasToken cannot be empty

Error message

sasToken cannot be empty

What it means

Thrown by the Azure remote-state backend's buildClient when config.SasToken is non-empty (so the SAS branch was selected) but strings.TrimSpace(sasToken) is empty, meaning the supplied value was only whitespace. The non-empty check that selected the branch passes for whitespace-only strings, so this guard catches the degenerate case explicitly.

Solutions

  1. Provide a complete, non-whitespace SAS token (typically starts with 'sv=' or '?' followed by key=value pairs).
  2. Trim and validate the secret at the CI/secret store before injecting it, so whitespace-only values fail earlier.
  3. If you did not mean to use SAS, unset sas_token / ARM_SAS_TOKEN so a different auth branch (access key, AAD, ARM-fallback) is selected.
  4. Prefer use_azuread_authentication=true with proper SP credentials for less error-prone auth.

Example fix

# before: secret resolved to spaces
export ARM_SAS_TOKEN="   "   # -> sasToken cannot be empty
# after: real token (with or without leading '?')
export ARM_SAS_TOKEN="sv=2021-06-08&ss=bfqt&srt=sco&sp=rwdlacupiydd&se=...&sig=..."
Defensive patterns

Strategy: validation

Validate before calling

// Validate a SAS token env before terraform runs.
func validSAS(v string) error {
    if strings.TrimSpace(v) == "" { return fmt.Errorf("SAS token is empty/whitespace") }
    return nil
}

Type guard

null

Try / catch

client, err := azure.NewClient(ctx, cfg)
if err != nil && strings.Contains(err.Error(), "sasToken cannot be empty") {
    // re-check the injected secret and surface a clearer message
}

Prevention

When it happens

Trigger: sas_token in the azurerm backend block, or the matching env var (ARM_SAS_TOKEN), is set to a string of only spaces, tabs, or newlines. The switch case config.SasToken != "" is taken, then the trim check fails.

Common situations: A CI secret that resolved to whitespace (e.g. a quoted space in the CI variable definition), copy-paste of just the leading '?' of a SAS query string without the rest, or a templating system that left blanks. Also seen when the env var inherited a stray trailing newline combined with no real token.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/5cf23ec9d975fe39. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/azure/api_client.go:57

	accessKey          string
	sasToken           string
	azureAdStorageAuth auth.Authorizer
}

func buildClient(ctx context.Context, config BackendConfig) (*Client, error) {
	client := Client{
		environment:        config.AuthConfig.Environment,
		storageAccountName: config.StorageAccountName,
	}

	var armAuthRequired bool
	switch {
	case config.AccessKey != "":
		client.accessKey = config.AccessKey
	case config.SasToken != "":
		sasToken := config.SasToken
		if strings.TrimSpace(sasToken) == "" {
			return nil, fmt.Errorf("sasToken cannot be empty")
		}
		client.sasToken = strings.TrimPrefix(sasToken, "?")
	case config.UseAzureADAuthentication:
		var err error
		client.azureAdStorageAuth, err = auth.NewAuthorizerFromCredentials(ctx, *config.AuthConfig, config.AuthConfig.Environment.Storage)
		if err != nil {
			return nil, fmt.Errorf("unable to build authorizer for Storage API: %+v", err)
		}
	default:
		// AAD authentication (ARM scope) is required only when no auth method is specified, which falls back to listing the access key via ARM API.
		armAuthRequired = true
	}

	// If `config.LookupBlobEndpoint` is true, we need to authenticate with ARM to lookup the blob endpoint
	if config.LookupBlobEndpoint {
		armAuthRequired = true
	}

View on GitHub (pinned to d32a084675)