hashicorp/nomad · error
multiple lock operations
Error message
multiple lock operations
What it means
getLockOperation in command/agent/variable_endpoint.go determines which single lock operation (renew, acquire, release) a variable request carries. If more than one of the renewLock/acquireLock/releaseLock flags is set, the request is ambiguous and the function returns this error. A variable lock request must express exactly zero or one lock operation.
Source
Thrown at command/agent/variable_endpoint.go:298
}
func isOneAndOnlyOneSet(a, b, c bool) bool {
return (a || b || c) && !a != !b != !c != !(a && b && c)
}
// getLockOperation returns the lock operation to be performed in case there is
// one. It returns error if more than one is set.
func getLockOperation(queryParams url.Values) (string, error) {
_, renewLock := queryParams[renewLockQueryParam]
_, acquireLock := queryParams[acquireLockQueryParam]
_, releaseLock := queryParams[releaseLockQueryParam]
if !renewLock && !acquireLock && !releaseLock {
return "", nil
}
if !isOneAndOnlyOneSet(renewLock, acquireLock, releaseLock) {
return "", errors.New("multiple lock operations")
}
switch {
case renewLock:
return renewLockQueryParam, nil
case acquireLock:
return acquireLockQueryParam, nil
case releaseLock:
return releaseLockQueryParam, nil
default:
return "", errors.New("unspecified lock operation")
}
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Send only one lock operation per request: set exactly one of acquire, release, or renew and leave the others unset.
- Split combined operations into sequential requests (acquire first, then later renew/release).
- Audit client code/automation for defaults that set multiple lock booleans (e.g. `lock: true, unlock: true`).
- Check for zero values being explicitly serialized: omit unset lock fields instead of sending explicit false/true pairs.
Example fix
// before: ambiguous request POST /v1/var/path?lock=true&unlock=true // after: single operation per call POST /v1/var/path?lock=true POST /v1/var/path?unlock=true
Defensive patterns
Strategy: validation
Validate before calling
ops := []bool{renew, acquire, release}
count := 0
for _, o := range ops {
if o {
count++
}
}
if count > 1 {
return errors.New("send only one lock operation per variable request")
} Try / catch
if err := updateVariable(req); err != nil {
if strings.Contains(err.Error(), "multiple lock operations") {
return fmt.Errorf("split request: set only one of lock/unlock/renew per call")
}
return err
} Prevention
- Model lock operations as an enum so only one can ever be set.
- Omit unset lock fields in serialized requests instead of sending explicit flags.
- Review automation scripts that build lock query parameters.
When it happens
Trigger: Issuing a variable write/update via VariableSpecificRequest with two or more lock query flags simultaneously — e.g. a request setting both `lock=true` (acquire) and `unlock=true` (release), or renew plus release in the same body/query.
Common situations: Hand-built API calls combining lock query parameters; templating bugs where a client sets all lock fields to the same boolean; retry logic re-sending a request that merges two operations; misuse of the variables HTTP API with both lock and unlock params.
Related errors
- Lock delay and TTL must be positive
- TTL must be between 10 seconds and 24 hours
- LockNoPathErr
- unspecified lock operation
- missing path
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/d4d3ee4797784ad3.
Report an issue: GitHub.