t8y2/dbx · error

Root znode cannot be modified

Error message

Root znode cannot be modified

What it means

put normalizes the request key into a znode path and refuses to write the root: if path == "/" it returns 'Root znode cannot be modified'. The ZooKeeper root znode is structural and this driver forbids creating/overwriting it, so keys that normalize to the root are rejected before any server call.

Source

Thrown at agents/drivers/zookeeper/operations.go:100

		"found":    true,
		"key":      path,
		"value":    encodeValue(data),
		"metadata": statMetadata(stat),
	}, nil
}

func (service *server) put(params json.RawMessage) (map[string]any, error) {
	client, err := service.requireClient()
	if err != nil {
		return nil, err
	}
	var request putRequest
	if err := json.Unmarshal(params, &request); err != nil {
		return nil, err
	}
	path := normalizePath(request.Key)
	if path == "/" {
		return nil, errors.New("Root znode cannot be modified")
	}
	data, err := decodeValue(request.Value)
	if err != nil {
		return nil, err
	}
	writeMode := request.WriteMode
	if writeMode == "" {
		writeMode = "upsert"
	}
	switch writeMode {
	case "create":
		return createNode(client, path, data, request.CreateMode)
	case "update":
		stat, err := client.Set(path, data)
		if err != nil {
			return nil, err
		}
		return putResult(stat), nil

View on GitHub (pinned to c0390bff16)

Solutions

  1. Provide a non-root key, e.g. "/myapp/config" instead of "/".
  2. Guard your caller code to reject empty or "/" keys before invoking put.
  3. Check how the key is assembled (prefix + name) — an unset prefix/name variable often collapses the path to "/".
  4. Remember ZooKeeper namespaces are hierarchical: pick a dedicated parent znode for application data.

Example fix

// before
if key == "" { key = "/" }
err := driver.Put(ctx, key, value)
// after
if key == "" { return fmt.Errorf("key must not be empty") }
err := driver.Put(ctx, "/myapp/"+key, value)
Defensive patterns

Strategy: validation

Validate before calling

func validatePutKey(key string) error {
    k := strings.TrimSpace(key)
    if k == "" || k == "/" {
        return errors.New("zookeeper put key must identify a non-root znode")
    }
    return nil
}

Try / catch

if err := validatePutKey(key); err != nil { return err }
_, err := driver.Put(ctx, key, value)
if err != nil && strings.Contains(err.Error(), "Root znode cannot be modified") {
    return fmt.Errorf("refusing to write znode root; supply a child path: %w", err)
}

Prevention

When it happens

Trigger: Calling put (directly or via dispatch) with Key = "/" or an empty/blank key that normalizePath resolves to "/". Raised in agents/drivers/zookeeper/operations.go:100.

Common situations: Empty key field in a JSON put request; key built by string concatenation where the prefix variable was empty ("" + "/" remaining as "/"); users intending 'the namespace root' and passing "/"; path-join bugs trimming the actual node name.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/408a0ea4b249f567. Report an issue: GitHub.