hashicorp/nomad · warning

Task bucket doesn't exist and transaction is not writable

Error message

Task bucket doesn't exist and transaction is not writable

What it means

Thrown by getTaskBucket when the task-level bucket (taskBucketName(taskName)) is missing under the allocation bucket and the bolt transaction is not writable. Writable transactions auto-create the task bucket; read-only ones cannot, so the error is returned. It is the deepest of the three bucket-resolution errors in the allocations -> allocID -> taskName hierarchy.

Source

Thrown at client/state/db_bolt.go:827

}

// getTaskBucket returns the bucket used to persist state about a
// particular task. If the root allocation bucket, the specific
// allocation or task bucket doesn't exist, they will be created as long as the
// transaction is writable.
func getTaskBucket(tx *boltdd.Tx, allocID, taskName string) (*boltdd.Bucket, error) {
	alloc, err := getAllocationBucket(tx, allocID)
	if err != nil {
		return nil, err
	}

	// Retrieve the specific task bucket
	w := tx.Writable()
	key := taskBucketName(taskName)
	task := alloc.Bucket(key)
	if task == nil {
		if !w {
			return nil, fmt.Errorf("Task bucket doesn't exist and transaction is not writable")
		}

		task, err = alloc.CreateBucket(key)
		if err != nil {
			return nil, err
		}
	}

	return task, nil
}

// PutDevicePluginState stores the device manager's plugin state or returns an
// error.
func (s *BoltStateDB) PutDevicePluginState(ps *dmstate.PluginState) error {
	return s.db.Update(func(tx *boltdd.Tx) error {
		// Retrieve the root device manager bucket
		devBkt, err := tx.CreateBucketIfNotExists(devManagerBucket)
		if err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure reads for a task happen only after at least one successful Update that creates the task bucket
  2. Verify taskName spelling/casing matches what the runner persisted
  3. Use a writable Update transaction if bucket creation is expected
  4. Treat the error as 'no persisted state for this task' in read paths

Example fix

// before
var ts *structs.TaskState
tx.Bucket(allocationsBucketName).Bucket([]byte(allocID)).Bucket(taskBucketName(taskName)).Get(taskStateKey)
// after: use getTaskBucket-aware API in a writable tx
ts, err := GetTaskState(db, allocID, taskName)
if err != nil {
    if strings.Contains(err.Error(), "Task bucket doesn't exist") {
        ts = nil // no state persisted yet
    } else {
        return err
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if taskName == "" {
    return errors.New("taskName must be non-empty before reading task state")
}
// ensure at least one prior Update created the task bucket
persisted, err := taskBucketExists(db, allocID, taskName)
if err != nil {
    return err
}
if !persisted {
    return nil
}

Type guard

func isMissingTaskBucketErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "Task bucket doesn't exist")
}

Try / catch

state, err := db.GetTaskState(tx, allocID, taskName)
if isMissingTaskBucketErr(err) {
    state = nil // no state persisted for this task yet
    err = nil
} else if err != nil {
    return err
}

Prevention

When it happens

Trigger: Reading task state/local state via a View() transaction for a task that never persisted state (bucket never created), or whose task bucket was deleted; also reached from putTaskRunnerLocalStateImpl/putTaskStateImpl when their callers run in read-only txs.

Common situations: Querying task state for a task name with a typo or one that was restarted before first persist; reading state right after alloc bucket creation but before any task write; migrating old alloc layouts where task buckets are absent.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/c9b46ab96450fabf. Report an issue: GitHub.