apache/answer · error

failed to open source file

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.

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.

Appendix: 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)