charmbracelet/crush · error

failed to create parent directories: %w

Error message

failed to create parent directories: %w

What it means

After verifying the target doesn't exist, createNewFile runs os.MkdirAll on the file's parent directory and wraps any failure. The wrapped error contains the OS reason: a path component exists as a file, the directory isn't writable, or the filesystem is read-only.

Source

Thrown at internal/agent/tools/edit.go:120

			return response, nil
		},
	)
}

func createNewFile(edit editContext, filePath, content string, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
	fileInfo, err := os.Stat(filePath)
	if err == nil {
		if fileInfo.IsDir() {
			return fantasy.NewTextErrorResponse(fmt.Sprintf("path is a directory, not a file: %s", filePath)), nil
		}
		return fantasy.NewTextErrorResponse(fmt.Sprintf("file already exists: %s", filePath)), nil
	} else if !os.IsNotExist(err) {
		return fantasy.ToolResponse{}, fmt.Errorf("failed to access file: %w", err)
	}

	dir := filepath.Dir(filePath)
	if err = os.MkdirAll(dir, 0o755); err != nil {
		return fantasy.ToolResponse{}, fmt.Errorf("failed to create parent directories: %w", err)
	}

	sessionID := GetSessionFromContext(edit.ctx)
	if sessionID == "" {
		return fantasy.ToolResponse{}, fmt.Errorf("session ID is required for creating a new file")
	}

	_, additions, removals := diff.GenerateDiff(
		"",
		content,
		strings.TrimPrefix(filePath, edit.workingDir),
	)
	p, err := edit.permissions.Request(
		edit.ctx,
		permission.CreatePermissionRequest{
			SessionID:   sessionID,
			Path:        fsext.PathOrPrefix(filePath, edit.workingDir),
			ToolCallID:  call.ID,

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the wrapped error; if EEXIST, the parent name collides with an existing file — pick a different path
  2. Check directory write permissions (ls -ld)
  3. Confirm the filesystem is writable (mount options)
  4. Create the directory manually first if permissions need adjustment

Example fix

// before
file_path: "README.md/new.go"   // README.md is a file
// after
file_path: "internal/new.go"
Defensive patterns

Strategy: validation

Validate before calling

parent := filepath.Dir(target)
if fi, err := os.Stat(parent); err == nil && !fi.IsDir() {
    return fmt.Errorf("%s already exists and is not a directory", parent)
}
if f, err := os.OpenFile(parent, os.O_RDONLY, 0); err != nil {
    return fmt.Errorf("parent %s not usable: %v", parent, err)
} else { f.Close() }

Try / catch

if err := os.MkdirAll(dir, 0o755); err != nil {
    if errors.Is(err, fs.ErrExist) {
        return fmt.Errorf("path component %s is a file, not a directory", dir)
    }
    return err
}

Prevention

When it happens

Trigger: Parent directory of the new file cannot be created: e.g. creating 'src/newfile.go' when 'src' is an existing file; write-protected parent; read-only mount; ENOSPC.

Common situations: LLM chose a file_path whose parent collides with an existing file; scaffolding into vendor/ or read-only directories; container filesystem read-only; path contains a symlink to a non-directory.

Related errors


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