shadow1ng/fscan · error

failed to write realtime backup: %w

Error message

failed to write realtime backup: %w

What it means

This error is returned by JSON-less (text) writers when writing a formatted line to the realtime backup file (.realtime.tmp) fails. The library keeps a realtime crash-safety backup of every scan result, so any disk write failure is surfaced immediately instead of being silently buffered. It wraps the underlying OS error via %w.

Source

Thrown at common/output/writers.go:137

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)
		}
	}

	return nil
}

// getSeparator 获取分隔线文本
func (w *TXTWriter) getSeparator(newType ResultType) string {
	switch newType {
	case TypeHost:
		return i18n.GetText("output_section_hosts")
	case TypePort:
		return i18n.GetText("output_section_ports")
	case TypeService:
		return i18n.GetText("output_section_services")

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Check disk space (df -h) and free space on the output volume
  2. Verify the output directory still exists and the process has write permission for the .realtime.tmp file
  3. Inspect the wrapped error (%w / errors.Unwrap) for the exact OS-level cause (ENOSPC, EBADF, EACCES)
  4. Reopen or recreate the writer pointing to a valid writable path
  5. If backup durability is not required, use a writer variant without realtime backup

Example fix

// before
if _, err := w.realtimeFile.WriteString(line + "\n"); err != nil {
	return fmt.Errorf("failed to write realtime backup: %w", err)
}
// after
if _, err := w.realtimeFile.WriteString(line + "\n"); err != nil {
	if errors.Is(err, syscall.ENOSPC) {
		w.disableRealtimeBackup() // fall back to memory-only buffering
	} else {
		return fmt.Errorf("failed to write realtime backup: %w", err)
	}
}
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := os.Stat(outDir)
if err != nil || !info.IsDir() { return fmt.Errorf("output dir missing") }
if unix.Access(outDir, unix.W_OK) != nil { return fmt.Errorf("output dir not writable") }
usable := syscall.Statfs_t{}
syscall.Statfs(outDir, &usable)
if usable.Bavail*uint64(usable.Bsize) < 64<<20 { return fmt.Errorf("low disk space") }

Try / catch

if err := writer.Write(result); err != nil {
	var pe *fs.PathError
	if errors.As(err, &pe) && errors.Is(pe.Err, syscall.ENOSPC) {
		// switch output volume or abort
	}
	return fmt.Errorf("backup write failed: %w", err)
}

Prevention

When it happens

Trigger: Calling Write() on a writer whose realtimeFile is non-nil and whose underlying WriteString fails: disk full, file descriptor closed/invalid, permission revoked mid-run, or I/O error on the backing device.

Common situations: Disk quota exceeded during long scans; the .realtime.tmp file deleted or its directory removed while the process runs; running out of file descriptors after opening many writers; read-only filesystem remount.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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