apache/answer · error

failed to open source file %s: %w

Error message

failed to open source file %s: %w

What it means

This error is wrapped and returned by the embedded-FS copy helper in internal/cli/build.go when sourceFs.Open(srcPath) fails while walking the source tree during a build/copy operation. The source filesystem is typically an embed.FS, so files were compiled into the binary, but the joined path (sourceDir + walked path) does not resolve inside that FS — usually a path-joining bug rather than a missing file on disk. The underlying OS/FS error is preserved via %w.

Source

Thrown at internal/cli/build.go:536

		// Construct the absolute path for the source file/directory
		srcPath := filepath.Join(sourceDir, path)
		srcPath = filepath.ToSlash(srcPath)

		// Construct the absolute path for the destination file/directory
		dstPath := filepath.Join(targetDir, path)

		if d.IsDir() {
			// Create the directory in the destination
			err := os.MkdirAll(dstPath, os.ModePerm)
			if err != nil {
				return fmt.Errorf("failed to create directory %s: %w", dstPath, err)
			}
		} else {
			// Open the source file
			srcFile, err := sourceFs.Open(srcPath)
			if err != nil {
				return fmt.Errorf("failed to open source file %s: %w", srcPath, err)
			}
			defer srcFile.Close()

			// Create the destination file
			dstFile, err := os.Create(dstPath)
			if err != nil {
				return fmt.Errorf("failed to create destination file %s: %w", dstPath, err)
			}
			defer dstFile.Close()

			// Copy the file contents
			_, err = io.Copy(dstFile, srcFile)
			if err != nil {
				return fmt.Errorf("failed to copy file contents from %s to %s: %w", srcPath, dstPath, err)
			}
		}

		return nil

View on GitHub (pinned to 3b9f137061)

Solutions

  1. Check the wrapped underlying error in the message to see whether it's ENOENT, ENOTDIR, or invalid path
  2. Verify sourceDir matches the embed.FS root exactly (no leading './' or duplicate slashes) before calling the copy helper
  3. Apply filepath.ToSlash to sourceDir itself before filepath.Join, and re-check the constructed srcPath
  4. If using os.DirFS, confirm the file actually exists on disk and is readable by the build process user

Example fix

// before
srcPath := filepath.Join(sourceDir, path)
// after
srcPath := filepath.ToSlash(filepath.Join(filepath.ToSlash(sourceDir), path))
Defensive patterns

Strategy: validation

Validate before calling

// before invoking the copy/build operation
if _, err := fs.Stat(sourceFs, filepath.ToSlash(sourceDir)); err != nil {
    return fmt.Errorf("source dir %q not found in source FS: %w", sourceDir, err)
}

Try / catch

if err := copyDir(sourceFs, sourceDir, targetDir); err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) {
        log.Printf("copy failed on path %s: %v", perr.Path, perr.Err)
    }
    return err
}

Prevention

When it happens

Trigger: fs.WalkDir over an embed.FS yields a path that, after filepath.Join(sourceDir, path) and filepath.ToSlash, does not exist in the source filesystem; e.g. double slashes, a wrong sourceDir prefix, or opening a path that WalkDir reported but Open cannot resolve (symlinks, case mismatch, Windows backslash leakage).

Common situations: Build-time asset copying where sourceDir was configured with a leading/trailing slash mismatch against the embed directive; cross-platform builds where path separators were not normalized; renamed or moved embedded directories after upgrading the codebase.

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 apache/answer@3b9f137061 (2026-09-05). Data as JSON: /api/errors/cf5341869f640477. Report an issue: GitHub.