{"record":{"id":"ec73d9e0118f673c","repo":"hashicorp/terraform","slug":"blob-metadata-q-was-empty","errorCode":null,"errorMessage":"blob metadata %q was empty","messagePattern":"blob metadata %q was empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/azure/client.go","lineNumber":228,"sourceCode":"\n\treturn info.ID, nil\n}\n\nfunc (c *RemoteClient) getLockInfo() (*statemgr.LockInfo, error) {\n\toptions := blobs.GetPropertiesInput{}\n\tif c.leaseID != \"\" {\n\t\toptions.LeaseID = &c.leaseID\n\t}\n\n\tctx := newCtx()\n\tblob, err := c.giovanniBlobClient.GetProperties(ctx, c.containerName, c.keyName, options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\traw := blob.MetaData[lockInfoMetaKey]\n\tif raw == \"\" {\n\t\treturn nil, fmt.Errorf(\"blob metadata %q was empty\", lockInfoMetaKey)\n\t}\n\n\tdata, err := base64.StdEncoding.DecodeString(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlockInfo := &statemgr.LockInfo{}\n\terr = json.Unmarshal(data, lockInfo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn lockInfo, nil\n}\n\n// writes info to blob meta data, deletes metadata entry if info is nil\nfunc (c *RemoteClient) writeLockInfo(info *statemgr.LockInfo) error {","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/azure/client.go#L210-L246","documentation":"Raised in RemoteClient.getLockInfo (client.go:226-229). To report who holds a lock, the backend reads the blob metadata key 'terraformlockid'; if that metadata value is empty (present-but-blank or absent) while the code expects lock info, it returns this error. It usually indicates a corrupt or partially-written lock: the lease exists but the lock metadata was never written or was cleared.","triggerScenarios":"Produced at client.go:226-228 when blob.MetaData[lockInfoMetaKey] (\"terraformlockid\") is the empty string. Hit when Lock/Unlock call getLockInfo to build a LockError or to validate the holder.","commonSituations":"A crashed Terraform process that acquired the lease but died before writeLockInfo completed; manual lease acquisition outside Terraform; an external tool that cleared the metadata; a partial write interrupted by network failure.","solutions":["Break the lease directly since the lock info is unrecoverable: az storage blob lease break --account-name <account> -c <container> -b <key>","Then re-run 'terraform init' and the original command","Avoid external tools that manipulate the state blob metadata or leases"],"exampleFix":"# the lock info metadata is empty, so force-unlock by ID won't work;\n# break the lease at the Azure layer instead\naz storage blob lease break \\\n  --account-name mystage --auth-mode login \\\n  -c tfstate -b \"prod.terraform.tfstate\"\n\n# verify it is unlocked, then retry\nterraform init\nterraform apply","handlingStrategy":"validation","validationCode":"# Detect a corrupt lock (lease present but empty metadata) before retrying\nMETA=$(az storage blob metadata show --account-name \"$ARM_STORAGE_ACCOUNT_NAME\" -c \"$ARM_CONTAINER_NAME\" -n \"$ARM_KEY\" --query 'terraformlockid' -o tsv 2>/dev/null)\nLEASE=$(az storage blob show --account-name \"$ARM_STORAGE_ACCOUNT_NAME\" -c \"$ARM_CONTAINER_NAME\" -n \"$ARM_KEY\" --query 'properties.lease.status' -o tsv 2>/dev/null)\n[ \"$LEASE\" = \"locked\" ] && [ -z \"$META\" ] && echo \"FAIL: corrupt lock (empty metadata) -> break lease (155)\" || echo \"OK\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never let external tools manipulate the state blob metadata or leases","If a Terraform run is killed, check whether it left a lease without metadata before retrying","Break the lease at the Azure layer when metadata is irrecoverable"],"tags":["azure","state","locking","metadata","corruption"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}