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
- Verify the filePath passed to Delete points to a leaf node, not a directory
- Delete the child nodes first, then the parent (or use a recursive delete helper)
- Check for leftover children (e.g. shard replication nodes) under the target path
- 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
- Only call Delete on leaf value nodes; use hierarchical delete helpers for prefixes
- List children before deleting a path to confirm it is a leaf
- Prefer vtctldclient keyspace/shard delete commands over raw topo Delete
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
- cannot watch directory %v in cell %v
- node %v is not locked
- ${objError.Message}
- there are no cells in the topo
- SrvVSchema has no entry for keyspace %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/9f4f62d0aadd28fe.
Report an issue: GitHub.