hashicorp/terraform · error

key can not start and end with '/'

Error message

key can not start and end with '/'

What it means

Thrown by the ValidateFunc for the 'key' field in the OSS backend when the state file key starts with '/' or ends with '/'. Note the error message says 'can not start AND end' but the code uses OR logic (HasPrefix OR HasSuffix), so either condition triggers it. This prevents malformed OSS object keys that would create ambiguous state paths.

Solutions

  1. Ensure the key does not start or end with '/' — use 'project/state.tfstate' instead of '/project/state.tfstate'.
  2. Verify the key includes the filename, not just a path prefix.
  3. Keep the default 'terraform.tfstate' if you don't need custom paths.

Example fix

// before
terraform {
  backend "oss" {
    key = "/project/terraform.tfstate"
  }
}
// after
terraform {
  backend "oss" {
    key = "project/terraform.tfstate"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate OSS backend key before Terraform init
func validateOSSKey(key string) error {
    if strings.HasPrefix(key, "/") {
        return fmt.Errorf("key must not start with '/'; got %q", key)
    }
    if strings.HasSuffix(key, "/") {
        return fmt.Errorf("key must not end with '/'; got %q (include a filename, not a directory)", key)
    }
    return nil
}

Prevention

When it happens

Trigger: ValidateFunc checks strings.HasPrefix(v, "/") or strings.HasSuffix(v, "/"). The user sets 'key' to '/terraform.tfstate', 'terraform/', or '/state/'. The default value is 'terraform.tfstate'.

Common situations: Developer uses leading '/' thinking of absolute paths ('/project/state.tfstate'). Developer uses trailing '/' treating key as a directory ('project/'). Copy-paste from S3 backend where key conventions differ. Confusion between OSS object keys and filesystem paths.

Related errors


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

Appendix: source

Thrown at internal/backend/remote-state/oss/backend.go:173

				Optional:    true,
				Description: "The directory where state files will be saved inside the bucket",
				Default:     "env:",
				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
					prefix := v.(string)
					if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
						return nil, []error{fmt.Errorf("workspace_key_prefix must not start with '/' or './'")}
					}
					return nil, nil
				},
			},

			"key": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "The path of the state file inside the bucket",
				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
					if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") {
						return nil, []error{fmt.Errorf("key can not start and end with '/'")}
					}
					return nil, nil
				},
				Default: "terraform.tfstate",
			},
			"tablestore_instance_name": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "The instance name of tableStore table belongs",
				Default:     "",
			},

			"tablestore_table": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "TableStore table for state locking and consistency",
				Default:     "",
			},

View on GitHub (pinned to d32a084675)