vitessio/vitess · error

delete(%v, %v) failed: it's a directory

Error message

delete(%v, %v) failed: it's a directory

What it means

memorytopo's Delete refuses to delete a node that is itself a directory (has children). File/path-based memorytopo implementations treat container nodes as directories, and only leaf nodes can be deleted with Delete.

Source

Thrown at go/vt/topo/memorytopo/file.go:289

		return err
	}

	// Get the parent dir.
	dir, file := path.Split(filePath)
	p := c.factory.nodeByPath(c.cell, dir)
	if p == nil {
		return topo.NewError(topo.NoNode, filePath)
	}

	// Get the existing file.
	n, ok := p.children[file]
	if !ok {
		return topo.NewError(topo.NoNode, filePath)
	}

	// Check if it's a directory.
	if n.isDirectory() {
		return fmt.Errorf("delete(%v, %v) failed: it's a directory", c.cell, filePath)
	}

	// Check the version.
	if version != nil && n.version != uint64(version.(NodeVersion)) {
		return topo.NewError(topo.BadVersion, filePath)
	}

	// Now we can delete.
	c.factory.recursiveDelete(n)

	// Call the watches
	for _, w := range n.watches {
		if w.contents != nil {
			w.contents <- &topo.WatchData{
				Err: topo.NewError(topo.NoNode, filePath),
			}
			close(w.contents)
		}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Verify the filePath passed to Delete points to a leaf node, not a directory
  2. Delete the child nodes first, then the parent (or use a recursive delete helper)
  3. Check for leftover children (e.g. shard replication nodes) under the target path
  4. Use topo commands that handle hierarchy (e.g. DeleteKeyspace/DeleteShard) instead of raw Delete

Example fix

// before
err := ts.Delete(ctx, cell, keyspacePath, nil)
// after
children, _ := ts.List(ctx, cell, keyspacePath)
for _, child := range children {
    ts.Delete(ctx, cell, child, nil)
}
err := ts.Delete(ctx, cell, keyspacePath, nil)
Defensive patterns

Strategy: type-guard

Validate before calling

_, err := ts.Get(ctx, cell, filePath)
if err != nil && topo.IsErrType(err, topo.NoNode) {
    return fmt.Errorf("path %s does not exist", filePath)
}

Type guard

func isDirectoryErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "it's a directory")
}

Try / catch

err := ts.Delete(ctx, cell, filePath, nil)
if err != nil && strings.Contains(err.Error(), "it's a directory") {
    // recursively delete children first, then retry
}

Prevention

When it happens

Trigger: Calling topo.Server.Delete (backed by memorytopo/file.go) on a path that contains child nodes, e.g. deleting a keyspace or shard directory path instead of a leaf value node.

Common situations: Application bug passing a parent path (keyspace/shard prefix) where a leaf path (individual node) was intended; leftover child nodes (e.g. replication records) under the path being cleaned up; using the file-based memorytopo for paths the regular memorytopo allowed.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/9f4f62d0aadd28fe. Report an issue: GitHub.