Introduction
Go 1.16 introduced the embed package for embedding static files into binaries at compile time. The most common error is pattern file.txt: no matching files found during compilation, or file not found at runtime when the embedded file cannot be accessed. These errors stem from misunderstanding how embed paths are resolved relative to the source file, not the working directory, and from incorrect glob patterns that do not match the intended files. Since embedding happens at compile time, runtime file-not-found errors mean the file was never embedded in the first place.
Symptoms
At compile time:
$ go build
main.go:10:12: pattern templates/index.html: no matching files foundOr:
main.go:10:12: pattern assets/*: no matching files foundOr at runtime:
$ ./myapp
2024/03/15 10:23:45 ERROR: open embedded config/default.yaml: file does not existOr the embed compiles but the file is not accessible:
```go //go:embed data/config.yaml var configData []byte
func main() { fmt.Println(len(configData)) // Prints 0 - file was not embedded } ```
Common Causes
- Path relative to source file, not working directory:
//go:embed config.yamllooks next to the .go file, not in the current directory - Glob pattern does not match any files:
assets/*does not matchassets/images/photo.jpgwithout** - File outside module directory: Embed cannot include files outside the Go module root
- Embedding into wrong variable type: Using
stringfor binary data or[]bytefor directory - Build tags exclude the file:
//go:buildconstraints on the .go file prevent embed compilation - Symlinks not followed: Embedded file is a symlink and the target is not accessible
Step-by-Step Fix
Step 1: Use correct path relative to the source file
myproject/
├── cmd/
│ └── server/
│ └── main.go // This file has the embed directive
├── config/
│ └── default.yaml // This file to embed
└── go.mod```go // cmd/server/main.go - WRONG //go:embed config/default.yaml // Looks for cmd/server/config/default.yaml var configFile []byte
// cmd/server/main.go - CORRECT //go:embed ../../config/default.yaml // Relative to this source file var configFile []byte ```
Step 2: Use embed.FS for directory embedding
```go package main
import ( "embed" "io/fs" "log" "net/http" )
//go:embed templates/* var templateFS embed.FS
//go:embed static/* var staticFS embed.FS
func main() { // Serve embedded static files staticSub, _ := fs.Sub(staticFS, "static") http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticSub))))
// Read embedded template tmpl, err := templateFS.ReadFile("templates/index.html") if err != nil { log.Fatalf("read template: %v", err) }
log.Printf("Template size: %d bytes", len(tmpl)) http.ListenAndServe(":8080", nil) } ```
Step 3: Handle build constraints correctly
```go //go:build !production
package main
import "embed"
// This embed only runs when NOT building with -tags production //go:embed config/development.yaml var configData []byte ```
For the production build:
```go //go:build production
package main
import "embed"
//go:embed config/production.yaml var configData []byte ```
Build with:
go build -tags production -o myapp-prod .
go build -o myapp-dev .Prevention
- Always verify embed paths are relative to the
.gosource file, not the project root - Use
//go:embed dir/*for single-level directory, or//go:embed all:dir/**for recursive - Add a test that reads every embedded file to verify embedding works
- Use
go build -vto see which files are being embedded - Verify embedded content with
strings myapp | grep "unique_string_from_file" - Document the directory structure and embed paths in a project README for new developers