slimtoolkit/slim · error

no layers

Error message

no layers

What it means

Engine.Build assembles an image from the provided layer blobs/tarballs. With an empty Layers slice the resulting image would have no filesystem content, so Build fails fast with 'no layers' to avoid producing a broken or empty image.

Source

Thrown at pkg/imagebuilder/internalbuilder/engine.go:58

	pushToRegistry bool) (*Engine, error) {

	engine := &Engine{
		ShowBuildLogs:  showBuildLogs,
		PushToDaemon:   pushToDaemon,
		PushToRegistry: pushToRegistry,
	}

	return engine, nil
}

func (ref *Engine) Build(options imagebuilder.SimpleBuildOptions) (*imagebuilder.ImageResult, error) {
	if len(options.ImageConfig.Config.Entrypoint) == 0 &&
		len(options.ImageConfig.Config.Cmd) == 0 {
		return nil, fmt.Errorf("missing startup info")
	}

	if len(options.Layers) == 0 {
		return nil, fmt.Errorf("no layers")
	}

	if len(options.Layers) > 255 {
		return nil, fmt.Errorf("too many layers")
	}

	switch options.ImageConfig.Architecture {
	case "":
		options.ImageConfig.Architecture = "amd64"
	case "arm64", "amd64":
	default:
		return nil, fmt.Errorf("bad architecture value")
	}

	var img v1.Image
	if options.From == "" {
		//same as FROM scratch
		img = empty.Image

View on GitHub (pinned to 81940d17fa)

Solutions

  1. Populate options.Layers with at least one layer archive before calling Build; verify layer creation returned non-empty output.
  2. Debug the upstream layer-generation code: check include/exclude globs and source paths so files are actually captured.
  3. If a truly empty image is intended, use a builder path that allows it or add a minimal layer containing the runtime files.

Example fix

// before
opts := imagebuilder.SimpleBuildOptions{ImageConfig: cfg} // Layers empty
res, err := engine.Build(opts) // no layers
// after
layer, err := archive.TarWithOptions(appDir, &archive.TarOptions{})
if err != nil { return err }
opts := imagebuilder.SimpleBuildOptions{ImageConfig: cfg, Layers: []string{layerPath}}
res, err := engine.Build(opts)
Defensive patterns

Strategy: validation

Validate before calling

func hasLayers(opts imagebuilder.SimpleBuildOptions) bool {
	return len(opts.Layers) > 0
}

if !hasLayers(opts) {
	return errors.New("refusing to build: no layers were produced upstream")
}
res, err := engine.Build(opts)

Try / catch

res, err := engine.Build(opts)
if err != nil {
	switch {
	case strings.Contains(err.Error(), "no layers"):
		return nil, fmt.Errorf("layer generation produced nothing; check source dirs and include globs: %w", err)
	case strings.Contains(err.Error(), "missing startup info"):
		return nil, fmt.Errorf("image config lacks CMD/ENTRYPOINT: %w", err)
	}
	return nil, err
}

Prevention

When it happens

Trigger: Calling engine.Build with SimpleBuildOptions.Layers of length 0 — e.g. layer archiving upstream produced nothing (empty source dir, all files filtered), or options were constructed without appending any layer entries.

Common situations: A COPY/collect step silently produced an empty directory that was then skipped; layer generation code filtered out every file (bad include patterns); config assembled programmatically with Layers forgotten while startup info was set.

Related errors


AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31). Data as JSON: /api/errors/a8d19f5e905df0b5. Report an issue: GitHub.