charmbracelet/crush · error

error creating directory: %w

Error message

error creating directory: %w

What it means

The write tool creates missing parent directories with os.MkdirAll(dir, 0o755) before writing the file. If that fails, the tool wraps the OS error and aborts. MkdirAll fails when it lacks permission on an ancestor, an ancestor exists as a non-directory, or the filesystem rejects the create.

Source

Thrown at internal/agent/tools/write.go:91

				modTime := fileInfo.ModTime().Truncate(time.Second)
				lastRead := filetracker.LastReadTime(ctx, sessionID, filePath)
				if modTime.After(lastRead) {
					return fantasy.NewTextErrorResponse(fmt.Sprintf("File %s has been modified since it was last read.\nLast modification: %s\nLast read: %s\n\nPlease read the file again before modifying it.",
						filePath, modTime.Format(time.RFC3339), lastRead.Format(time.RFC3339))), nil
				}

				oldContent, readErr := os.ReadFile(filePath)
				if readErr == nil && string(oldContent) == params.Content {
					return fantasy.NewTextErrorResponse(fmt.Sprintf("File %s already contains the exact content. No changes made.", filePath)), nil
				}
			} else if !os.IsNotExist(err) {
				return fantasy.ToolResponse{}, fmt.Errorf("error checking file: %w", err)
			}

			dir := filepath.Dir(filePath)
			if err = os.MkdirAll(dir, 0o755); err != nil {
				return fantasy.ToolResponse{}, fmt.Errorf("error creating directory: %w", err)
			}

			oldContent := ""
			if fileInfo != nil && !fileInfo.IsDir() {
				oldBytes, readErr := os.ReadFile(filePath)
				if readErr == nil {
					oldContent = string(oldBytes)
				}
			}

			diff, additions, removals := diff.GenerateDiff(
				oldContent,
				params.Content,
				strings.TrimPrefix(filePath, workingDir),
			)

			p, err := permissions.Request(
				ctx,

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Ensure the process user can create the directory: check ownership and write permission on the nearest existing ancestor.
  2. Fix the path so no intermediate component is a regular file.
  3. Check disk space/quota (df -h, ENOSPC).
  4. Write within the project working directory instead of system paths.

Example fix

// before
{"file_path": "/etc/myapp/config.json", "content": "{}"}

// after
{"file_path": "config/config.json", "content": "{}"}
Defensive patterns

Strategy: validation

Validate before calling

dir := filepath.Dir(targetPath)
if err := os.MkdirAll(dir, 0o755); err != nil {
    return fmt.Errorf("cannot create %s: %w", dir, err)
}

Try / catch

if err != nil {
    if errors.Is(err, fs.ErrPermission) {
        return fmt.Errorf("mkdir denied: run the agent as a user that owns the project dir")
    }
    if errors.Is(err, syscall.ENOTDIR) {
        return fmt.Errorf("a path component is a file, not a directory")
    }
    return err
}

Prevention

When it happens

Trigger: Target file_path has a directory component that does not exist and cannot be created — parent owned by another user/read-only mount, or an ancestor path is a regular file (ENOTDIR), or the filesystem is full (ENOSPC).

Common situations: Agent running as a non-root user writing under /etc or another root-owned path; read-only container filesystems; path like src/main.go/newdir/file.go where main.go is a file; disk quota exceeded.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/1e3097d6502482cd. Report an issue: GitHub.