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:

bash
$ go build
main.go:10:12: pattern templates/index.html: no matching files found

Or:

bash
main.go:10:12: pattern assets/*: no matching files found

Or at runtime:

bash
$ ./myapp
2024/03/15 10:23:45 ERROR: open embedded config/default.yaml: file does not exist

Or 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.yaml looks next to the .go file, not in the current directory
  • Glob pattern does not match any files: assets/* does not match assets/images/photo.jpg without **
  • File outside module directory: Embed cannot include files outside the Go module root
  • Embedding into wrong variable type: Using string for binary data or []byte for directory
  • Build tags exclude the file: //go:build constraints 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

bash
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:

bash
go build -tags production -o myapp-prod .
go build -o myapp-dev .

Prevention

  • Always verify embed paths are relative to the .go source 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 -v to 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