Billionmail/BillionMail · error

create thumbnail dir

Error message

create thumbnail dir: %w

What it means

GenerateThumbnail creates the parent directory of cfg.OutputPath via os.MkdirAll before invoking ImageMagick. If directory creation fails (permissions, read-only fs, path is a file), the OS error is wrapped with 'create thumbnail dir: %w'.

Solutions

  1. Inspect the wrapped OS error for the exact filesystem cause
  2. Ensure the thumbnail output directory exists and is writable by the process user
  3. Mount a writable volume for output paths in containers
  4. Verify OutputPath is constructed correctly (non-empty base dir, directory not a file)

Example fix

// before
if err := os.MkdirAll(filepath.Dir(cfg.OutputPath), 0755); err != nil {
	return "", fmt.Errorf("create thumbnail dir: %w", err)
}
// after
outDir := filepath.Dir(cfg.OutputPath)
if info, err := os.Stat(outDir); err == nil && !info.IsDir() {
	return "", fmt.Errorf("thumbnail output path %q is a file, not a directory", outDir)
}
if err := os.MkdirAll(outDir, 0755); err != nil {
	return "", fmt.Errorf("create thumbnail dir: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

outDir := filepath.Dir(cfg.OutputPath)
if outDir == "." || outDir == "" {
	return fmt.Errorf("thumbnail output path has no directory component")
}
if info, err := os.Stat(outDir); err == nil && !info.IsDir() {
	return fmt.Errorf("%s is a file", outDir)
}

Try / catch

path, err := GenerateThumbnail(ctx, cfg)
if err != nil && strings.HasPrefix(err.Error(), "create thumbnail dir:") {
	var perr *fs.PathError
	if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrPermission) {
		return fmt.Errorf("cannot write thumbnails to %s: check volume mount/permissions", filepath.Dir(cfg.OutputPath))
	}
}

Prevention

When it happens

Trigger: os.MkdirAll(filepath.Dir(cfg.OutputPath), 0755) fails: the parent of OutputPath is unwritable, already exists as a file, disk is full, or an invalid path segment is supplied.

Common situations: Same writable-volume issues as screenshot output in Docker; OutputPath built by joining a bad/empty base dir so Dir() resolves to an unwritable location; non-root process user lacking permissions.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/b003a84c3bedc459. Report an issue: GitHub.

Appendix: source

Thrown at core/internal/service/video_gen/thumbnail.go:58

	}
	h := cfg.Height
	if h == 0 {
		h = defaultThumbHeight
	}

	return []string{
		cfg.InputPath,
		"-resize", fmt.Sprintf("%dx%d!", w, h),
		"-quality", "90",
		cfg.OutputPath,
	}
}

// GenerateThumbnail creates a thumbnail from a screenshot using ImageMagick.
// Requires: magick (ImageMagick) installed.
func GenerateThumbnail(ctx context.Context, cfg ThumbnailConfig) (string, error) {
	if err := os.MkdirAll(filepath.Dir(cfg.OutputPath), 0755); err != nil {
		return "", fmt.Errorf("create thumbnail dir: %w", err)
	}

	args := BuildThumbnailArgs(cfg)
	cmd := exec.CommandContext(ctx, "magick", args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("imagemagick thumbnail failed: %w\noutput: %s", err, string(out))
	}

	return cfg.OutputPath, nil
}

View on GitHub (pinned to fc36c76c05)