hashicorp/terraform · error · LockError
state not locked
Error message
state not locked
What it means
A NestingSet block may not contain WriteOnly attributes. cty hoists value marks (including WriteOnly marks) up to the outer set value, so per-element WriteOnly cannot be tracked — only the whole set could be WriteOnly, which is not the intent. The validator calls blockS.Block.ContainsWriteOnly() and rejects.
Solutions
- Move the WriteOnly attribute to the outer (non-set) block so the mark applies there.
- Change the block from NestingSet to NestingList if per-element WriteOnly is required.
- Drop WriteOnly from the set's child attributes.
Example fix
// before
"secrets": {
Nesting: configschema.NestingSet,
Block: configschema.Block{Attributes: map[string]*configschema.Attribute{
"token": { Type: cty.String, Optional: true, WriteOnly: true },
}},
},
// after
"secrets": {
Nesting: configschema.NestingList,
Block: configschema.Block{Attributes: map[string]*configschema.Attribute{
"token": { Type: cty.String, Optional: true, WriteOnly: true },
}},
}, Defensive patterns
Strategy: validation
Validate before calling
// Reject NestingSet blocks that contain any WriteOnly attribute.
func setHasWriteOnly(nb *configschema.NestedBlock) bool {
if nb.Nesting != configschema.NestingSet { return false }
return nb.Block.ContainsWriteOnly()
} Type guard
func setFreeOfWriteOnly(nb *configschema.NestedBlock) bool {
return nb.Nesting != configschema.NestingSet || !nb.Block.ContainsWriteOnly()
} Prevention
- Keep WriteOnly attributes out of NestingSet blocks.
- Move secrets to the outer non-set block, or switch to NestingList.
- Audit schemas when introducing WriteOnly fields.
When it happens
Trigger: NestingSet block with any attribute having WriteOnly: true. Guard at internal_validate.go:100 is `blockS.Block.ContainsWriteOnly()`.
Common situations: Adding a secret WriteOnly attribute inside a set of credential blocks; reusing a NestingList-with-WriteOnly schema under a NestingSet.
Related errors
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/ade159f5c778d3b4.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/inmem/backend.go:205
// make a copy of the lock info to avoid any testing shenanigans
*lockErr.Info = *lockInfo
return "", lockErr
}
info.Created = time.Now().UTC()
l.m[name] = info
return info.ID, nil
}
func (l *lockMap) unlock(name, id string) error {
l.Lock()
defer l.Unlock()
lockInfo := l.m[name]
if lockInfo == nil {
return errors.New("state not locked")
}
lockErr := &statemgr.LockError{
Info: &statemgr.LockInfo{},
}
if id != lockInfo.ID {
lockErr.Err = errors.New("invalid lock id")
*lockErr.Info = *lockInfo
return lockErr
}
delete(l.m, name)
return nil
}
View on GitHub (pinned to d32a084675)