moonD4rk/HackBrowserData · error

copy: %w

Error message

copy: %w

What it means

Acquire wraps a copyFile failure with "copy: %w" when the platform is NOT Windows. On non-Windows systems there is no locked-file fallback, so any error from the plain file copy (open, read, or write failure) is surfaced under this prefix. On Windows this same prefix appears as one branch of a joined error when both the normal copy and the locked copy failed.

Source

Thrown at filemanager/session.go:49

// Acquire copies a browser file (or directory) from src to dst.
// For regular files, it also copies SQLite WAL and SHM companion files
// if they exist. For directories (e.g. LevelDB), it copies the entire
// directory while skipping lock files.
//
// On Windows, if the normal copy fails (e.g. file locked by Chrome),
// it falls back to DuplicateHandle + FileMapping to bypass exclusive locks.
func (s *Session) Acquire(src, dst string, isDir bool) error {
	if isDir {
		return copyDir(src, dst, "lock")
	}

	// Try normal copy first
	err := copyFile(src, dst)
	if err != nil {
		// Only attempt locked-file fallback on Windows where Chrome holds exclusive locks.
		// On other platforms, return the original error directly.
		if runtime.GOOS != "windows" {
			return fmt.Errorf("copy: %w", err)
		}
		if err2 := copyLocked(src, dst); err2 != nil {
			return errors.Join(
				fmt.Errorf("copy: %w", err),
				fmt.Errorf("locked copy: %w", err2),
			)
		}
	}

	// Copy SQLite WAL/SHM companion files if present
	var walErrs []error
	for _, suffix := range []string{"-wal", "-shm"} {
		walSrc := src + suffix
		if isFileExists(walSrc) {
			if err := copyFile(walSrc, dst+suffix); err != nil {
				walErrs = append(walErrs, fmt.Errorf("copy %s: %w", suffix, err))
			}
		}

View on GitHub (pinned to 0503d04d7a)

Solutions

  1. Verify the source file path exists and is readable by the current user (ls -l the profile directory).
  2. Ensure the destination (session temp dir) still exists and is writable — call session.Cleanup() only after extraction completes.
  3. Free disk space if the copy failed on write.
  4. On Windows, also read the joined "locked copy:" branch of the error to see why the fallback failed; restarting the browser or retrying often clears transient locks.

Example fix

// before
if err := s.Acquire(src, dst, false); err != nil {
    log.Fatalf("acquire failed: %v", err)
}
// after
if err := s.Acquire(src, dst, false); err != nil {
    if _, statErr := os.Stat(src); statErr != nil {
        log.Warnf("source missing, skipping: %v", statErr)
        return nil
    }
    return fmt.Errorf("acquire %s: %w", src, err)
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-check source readability and destination writability
if fi, err := os.Stat(src); err != nil || fi.IsDir() {
    return fmt.Errorf("src %s not a readable file: %w", src, err)
}
if err := os.MkdirAll(filepath.Dir(dst), 0o700); err != nil {
    return fmt.Errorf("dst dir: %w", err)
}

Try / catch

// Go
if err := s.Acquire(src, dst, false); err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) {
        log.Warnf("filesystem error on %s: %v", pathErr.Path, pathErr.Err)
    }
    return err
}

Prevention

When it happens

Trigger: Session.Acquire(src, dst, false) on Linux/macOS where copyFile fails: source file missing, source unreadable (permissions), destination not writable, or destination directory missing. On Windows it also appears whenever copyLocked also fails, producing errors.Join of both errors.

Common situations: Browser profile path changed in a recent browser version; running without read access to another user's profile; disk full; the SQLite database deleted while the browser was updating; destination temp dir removed mid-run.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of moonD4rk/HackBrowserData@0503d04d7a (2026-09-06). Data as JSON: /api/errors/29d1da2132d6069c. Report an issue: GitHub.