jaegertracing/jaeger · error

cannot open index.html: %w

Error message

cannot open index.html: %w

What it means

loadIndexHTML opens '/index.html' through the supplied http.FileSystem Open function and reads its contents. When Open itself fails — the file does not exist in the asset filesystem or cannot be opened — the function wraps the OS error as 'cannot open index.html'. A sibling error 'cannot read from index.html' covers failures after a successful open.

Source

Thrown at cmd/jaeger/internal/extension/jaegerquery/internal/static_handler.go:198

	out := h.indexHTMLRaw
	if cfg := h.getUIConfig(); cfg != nil {
		out = cfg.regexp.ReplaceAll(out, cfg.config)
	}
	versionJSON, _ := json.Marshal(version.Get())
	out = versionPattern.ReplaceAll(out, fmt.Appendf(nil, "JAEGER_VERSION = %s;", versionJSON))
	var caps BackendCapabilities
	if h.backendCaps != nil {
		caps = h.backendCaps(ctx)
	}
	capsJSON, _ := json.Marshal(caps)
	out = capabilitiesPattern.ReplaceAll(out, fmt.Appendf(nil, "JAEGER_BACKEND_CAPABILITIES = %s;", capsJSON))
	return out
}

func loadIndexHTML(open func(string) (http.File, error)) ([]byte, error) {
	indexFile, err := open("/index.html")
	if err != nil {
		return nil, fmt.Errorf("cannot open index.html: %w", err)
	}
	defer indexFile.Close()
	indexBytes, err := io.ReadAll(indexFile)
	if err != nil {
		return nil, fmt.Errorf("cannot read from index.html: %w", err)
	}
	return indexBytes, nil
}

func loadUIConfig(uiConfig string) (*loadedConfig, error) {
	if uiConfig == "" {
		return nil, nil
	}
	bytesConfig, err := os.ReadFile(filepath.Clean(uiConfig))
	if err != nil {
		return nil, fmt.Errorf("cannot read UI config file %v: %w", uiConfig, err)
	}
	var r []byte

View on GitHub (pinned to 806f444784)

Solutions

  1. Ensure index.html sits directly at the root of the configured assets directory (flatten nested dist/ folders if needed)
  2. Check permissions on index.html and its parent directories for the user running jaeger (namei -l <path>/index.html)
  3. If you do not need a custom UI, unset ui.assets-path so the embedded UI filesystem is used
  4. Verify the volume mount actually contains the UI files (kubectl exec / docker exec and ls the directory)

Example fix

// before: archive extracted keeping a nested folder
/assets-path/dist/index.html  # Open("/index.html") -> file does not exist
// after
mv assets-path/dist/* assets-path/   # /assets-path/index.html now resolvable
Defensive patterns

Strategy: validation

Validate before calling

// run before configuring the assets path
func canOpenIndexHTML(dir string) error {
	f, err := os.Open(filepath.Join(dir, "index.html"))
	if err != nil {
		return fmt.Errorf("cannot open index.html in %s: %w", dir, err)
	}
	defer f.Close()
	return nil
}

Try / catch

_, err := loadIndexHTML(assetsFS.Open)
if err != nil {
	var pathErr *fs.PathError
	if errors.As(err, &pathErr) && errors.Is(pathErr.Err, fs.ErrNotExist) {
		log.Fatalf("index.html missing under %s — check assets-path layout", pathErr.Path)
	}
	return err
}

Prevention

When it happens

Trigger: loadIndexHTML called (from newStaticAssetsHandler at boot, or the hot-reload path) while assetsFS.Open("/index.html") returns an error: the configured assets directory has no index.html at its root, the path is a directory mismatch, or permission/os errors prevent opening the file.

Common situations: Custom UI archives that extract to an inner folder (dist/index.html vs index.html); empty or wrongly mounted volumes in Kubernetes/Docker; SELinux or read-only mount permission issues; typos in the assets-path so http.Dir resolves to a nonexistent directory.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/d712a7c6e4278f9d. Report an issue: GitHub.