dagger/dagger · error

key %q is not set in user-level config for workspace %q

Error message

key %q is not set in user-level config for workspace %q

What it means

DeleteUserConfigValue removes a key under [workspaces.<workspaceKey>] in user-level config and then prunes emptied tables. Before deleting, it checks tree.GetPath; if the key does not exist it throws this error instead of silently succeeding, so callers can distinguish "removed" from "was never set".

Source

Thrown at core/workspace/userconfig.go:294

}

// DeleteUserConfigValue removes a config value under
// [workspaces.<workspaceKey>] in user-level config bytes, pruning any tables
// the removal leaves empty. It errors when the key is not set for that
// workspace.
func DeleteUserConfigValue(existing []byte, workspaceKey, key string) ([]byte, error) {
	parts, err := userOverlayKeyParts(key)
	if err != nil {
		return nil, err
	}
	tree, entryKey, err := userConfigTreeAndEntryKey(existing, workspaceKey)
	if err != nil {
		return nil, err
	}

	fullPath := append([]string{"workspaces", entryKey}, parts...)
	if tree.GetPath(fullPath) == nil {
		return nil, fmt.Errorf("key %q is not set in user-level config for workspace %q", key, entryKey)
	}
	if err := tree.DeletePath(fullPath); err != nil {
		return nil, fmt.Errorf("unset %q: %w", key, err)
	}
	// Prune tables the removal left empty, innermost first.
	for prefix := fullPath[:len(fullPath)-1]; len(prefix) > 0; prefix = prefix[:len(prefix)-1] {
		parent, ok := tree.GetPath(prefix).(*toml.Tree)
		if !ok || len(parent.Keys()) > 0 {
			break
		}
		if err := tree.DeletePath(prefix); err != nil {
			return nil, fmt.Errorf("prune empty table %q: %w", strings.Join(prefix, "."), err)
		}
	}

	out, err := tree.ToTomlString()
	if err != nil {
		return nil, fmt.Errorf("serialize user config: %w", err)

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Check the key exists first with GetPath at ["workspaces", entryKey, ...parts] and treat nil as already-deleted
  2. Verify the entryKey (workspace key) matches the one the value was written under
  3. Re-run with the exact key string used at write time (segments and casing must match)
  4. If the delete is idempotent-critical, catch this error and ignore it

Example fix

// before: unconditional delete fails on second run
if _, err := workspace.DeleteUserConfigValue(data, key, wsKey); err != nil { return err }
// after: tolerate not-set
if _, err := workspace.DeleteUserConfigValue(data, key, wsKey); err != nil && !strings.Contains(err.Error(), "is not set") { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

full := append([]string{"workspaces", entryKey}, parts...)
if tree.GetPath(full) == nil {
	return nil // already unset; nothing to do
}

Try / catch

_, err := workspace.DeleteUserConfigValue(data, key, wsKey)
if err != nil && strings.Contains(err.Error(), "is not set") {
	return data, nil // treat as idempotent success
}

Prevention

When it happens

Trigger: DeleteUserConfigValue called for a key that was never written for that workspace, a typo'd key, a key belonging to a different entryKey (workspace), or after the key was already deleted in a prior call.

Common situations: Running an unset command twice; unsetting a key with a slightly different path than it was set with; switching workspaces (entryKey) and assuming settings carry over; concurrent edits where another process already removed the key.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/58c7f2d95fa1b931. Report an issue: GitHub.