hashicorp/terraform · error
key can not start and end with '/'
Error message
key can not start and end with '/'
What it means
Schema-level ValidateFunc for the COS backend 'key' attribute (the object filename). Leading or trailing slashes would create an empty path segment or a directory-like key, so they are rejected at terraform init time.
Solutions
- Use a bare filename or a slash-separated path with no leading or trailing slash, e.g. 'terraform.tfstate' or 'prod/terraform.tfstate'.
- Re-run terraform init.
Example fix
// before
terraform {
backend "cos" {
key = "/terraform.tfstate"
}
}
// after
terraform {
backend "cos" {
key = "terraform.tfstate"
}
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the COS backend key before terraform init.
func validateCOSKey(key string) error {
if strings.HasPrefix(key, "/") || strings.HasSuffix(key, "/") {
return fmt.Errorf("key can not start and end with '/'")
}
return nil
} Prevention
- Use a bare filename or a slash-separated path with no leading/trailing slash.
- Lint backend blocks in CI.
- Remember COS keys are object names, not POSIX paths.
- Keep key stable for the life of the workspace; renaming is a migration.
When it happens
Trigger: terraform init with a `key` value starting with '/' or ending with '/'.
Common situations: User sets key = '/terraform.tfstate' by analogy with S3; sets key = 'envs/' intending a folder; copies a full path with slashes at both ends.
Related errors
- acl value invalid, expected
- Invalid URL: must be
- prefix must not start with '/' or './'
- cannot be higher than
- cannot be lower than
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/eb507710e600047a.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/cos/backend.go:148
Type: schema.TypeString,
Optional: true,
Description: "The directory for saving the state file in bucket",
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
prefix := v.(string)
if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
return nil, []error{fmt.Errorf("prefix must not start with '/' or './'")}
}
return nil, nil
},
},
"key": {
Type: schema.TypeString,
Optional: true,
Description: "The path for saving the state file in bucket",
Default: "terraform.tfstate",
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
},
},
"encrypt": {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to enable server side encryption of the state file",
Default: true,
},
"acl": {
Type: schema.TypeString,
Optional: true,
Description: "Object ACL to be applied to the state file",
Default: "private",
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
value := v.(string)
if value != "private" && value != "public-read" {View on GitHub (pinned to d32a084675)