shadow1ng/fscan · error

writer is closed

Error message

writer is closed

What it means

State guard in TXTWriter.Write: an attempt was made to write a scan result after the writer had already been closed (Close sets w.closed). Once closed the buffered file and realtime backup file are no longer valid for writes, so any further Write is rejected.

Source

Thrown at common/output/writers.go:124

		bufWriter:    bufio.NewWriter(file),
		buffer:       NewResultBuffer(),
		realtimeFile: realtimeFile,
		realtimePath: realtimePath,
	}, nil
}

// WriteHeader 写入头部
func (w *TXTWriter) WriteHeader() error {
	return nil
}

// Write 收集扫描结果到分类缓冲,同时实时备份
func (w *TXTWriter) Write(result *ScanResult) error {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.closed {
		return fmt.Errorf("writer is closed")
	}
	if result == nil {
		return fmt.Errorf("result cannot be nil")
	}

	// 1. 加入内存分类缓冲(用于最终有序输出)
	w.buffer.Add(result)

	// 2. 实时写入备份文件(防崩溃丢数据)
	if w.realtimeFile != nil {
		line := w.formatLine(result)
		if _, err := w.realtimeFile.WriteString(line + "\n"); err != nil {
			return fmt.Errorf("failed to write realtime backup: %w", err)
		}
		if err := w.realtimeFile.Sync(); err != nil {
			return fmt.Errorf("failed to sync realtime backup: %w", err)
		}
	}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Stop all producers before closing the writer (use WaitGroup or done channel)
  2. Check writer lifecycle: create a new writer for a new run
  3. Reorder shutdown so Close is the last operation after all writes

Example fix

// before
go writeResults(w); w.Close() // workers may write after close
// after
wg.Wait() // producers done
w.Close()
Defensive patterns

Strategy: validation

Try / catch

if err := w.Write(res); err != nil {
    if strings.Contains(err.Error(), "writer is closed") {
        return errors.New("write after close: producer lifecycle bug")
    }
    return err
}

Prevention

When it happens

Trigger: Calling Write after TXTWriter.Close(); manager Close() cascading to writer close while workers still emit results; reusing a writer created via NewTXTWriter across runs.

Common situations: Producer goroutines outliving writer shutdown, double-close-then-write patterns, using the Manager after Flush/Close while writers are already closed.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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