shadow1ng/fscan · error

failed to create CSV file: %w

Error message

failed to create CSV file: %w

What it means

NewCSVWriter creates (truncating) the main CSV output file; this error is returned when os.OpenFile fails. The constructor aborts and returns the wrapped OS error, since a CSV writer cannot function without its output file.

Source

Thrown at common/output/writers.go:612

// CSVWriter CSV格式写入器(分类去重)
// 双写机制:内存分类缓冲 + 实时NDJSON备份
type CSVWriter struct {
	file         *os.File
	bufWriter    *bufio.Writer
	csvWriter    *csv.Writer
	mu           sync.Mutex
	closed       bool
	buffer       *ResultBuffer
	realtimeFile *os.File // 实时备份文件(NDJSON格式)
	realtimePath string   // 实时备份文件路径
}

// NewCSVWriter 创建CSV写入器
func NewCSVWriter(filePath string) (*CSVWriter, error) {
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, DefaultFilePermissions)
	if err != nil {
		return nil, fmt.Errorf("failed to create CSV file: %w", err)
	}

	// 创建实时备份文件(NDJSON格式)
	realtimePath := filePath + ".realtime.tmp"
	realtimeFile, err := os.OpenFile(realtimePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, DefaultFilePermissions)
	if err != nil {
		file.Close()
		return nil, fmt.Errorf("failed to create realtime backup file: %w", err)
	}

	bufWriter := bufio.NewWriter(file)
	csvWriter := csv.NewWriter(bufWriter)

	return &CSVWriter{
		file:         file,
		bufWriter:    bufWriter,
		csvWriter:    csvWriter,
		buffer:       NewResultBuffer(),

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Create the parent directory first with os.MkdirAll(filepath.Dir(filePath), 0o755)
  2. Verify filePath is a valid writable file path, not a directory
  3. Check filesystem permissions for the running user
  4. Inspect the wrapped error (ENOENT/EACCES/ENOSPC) for the specific cause
  5. Check ulimit -n if the process holds many open files

Example fix

// before
w, err := output.NewCSVWriter(cfg.OutputPath)
// after
if err := os.MkdirAll(filepath.Dir(cfg.OutputPath), 0o755); err != nil {
	return err
}
w, err := output.NewCSVWriter(cfg.OutputPath)
Defensive patterns

Strategy: validation

Validate before calling

if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil { return err }
if fi, err := os.Stat(filePath); err == nil && fi.IsDir() { return fmt.Errorf("CSV path is a directory: %s", filePath) }
probe, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { return err }
probe.Close()

Try / catch

w, err := output.NewCSVWriter(path)
if err != nil {
	var pe *fs.PathError
	if errors.As(err, &pe) {
		if errors.Is(pe.Err, syscall.ENOENT) { os.MkdirAll(filepath.Dir(path), 0o755) }
		if errors.Is(pe.Err, syscall.EACCES) { return fmt.Errorf("check permissions on %s", pe.Path) }
	}
	return err
}

Prevention

When it happens

Trigger: os.OpenFile(filePath, O_CREATE|O_WRONLY|O_TRUNC) fails: parent directory missing, no write permission, filePath is an existing directory, or fd limit exhausted.

Common situations: Output path configured as a directory or pointing at a nonexistent folder; running as a user without permission on the output directory; path typos; read-only container volume.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/3b95fe204d591ad4. Report an issue: GitHub.