cilium/cilium · error

external types needed but types package is empty

Error message

external types needed but types package is empty

What it means

dpgen detected that the config struct references external struct/union types (needExternalTypes was set because a config variable is a struct or union that must be rendered separately by `dpgen type`), but the --types-pkg flag was not provided, so the generated import for the types package would be empty. Generation aborts rather than emitting code that references types from an unknown package.

Source

Thrown at tools/dpgen/config.go:66

	s, err := varsToStruct(spec, configOpts.outName, configOpts.kind, configOpts.typesPkg, configOpts.embeds)
	if err != nil {
		return fmt.Errorf("generating config struct: %w", err)
	}

	if needUtils {
		if err := writeUtilFile("util_generated.go", configOpts.goPkg); err != nil {
			return fmt.Errorf("writing util file: %w", err)
		}
	}

	var b bytes.Buffer
	if err := writeHeader(&b, configOpts.goPkg, nil); err != nil {
		return fmt.Errorf("writing header: %w", err)
	}

	if needExternalTypes {
		if configOpts.typesPkg == "" {
			return fmt.Errorf("external types needed but types package is empty")
		}
		if err := writeImports(&b, []string{configOpts.typesPkg}); err != nil {
			return fmt.Errorf("writing imports: %w", err)
		}
	}
	if _, err := b.WriteString(s); err != nil {
		return fmt.Errorf("writing struct: %w", err)
	}
	if err := os.WriteFile(configOpts.outFile, b.Bytes(), 0644); err != nil {
		return fmt.Errorf("writing output file: %w", err)
	}

	return nil
}

//go:embed util_generated.go.tpl
var utilTpl string

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Pass --types-pkg pointing at the Go package that will contain the generated types, e.g. --types-pkg github.com/cilium/cilium/pkg/datapath/config/types
  2. Also run `dpgen type` for each referenced struct/union so the types package actually contains those types
  3. Regenerate the dpgen invocation in the Makefile/build script to include the flag once any struct-typed config exists
  4. If no struct/union config variables are intended, change the offending variable to a scalar type so needExternalTypes stays false

Example fix

// before
dpgen config bpf_bpf_node_config.o node_config.go --out-name NodeConfig --kind "cilium.io/v2:NodeConfig"
// after
dpgen config bpf_bpf_node_config.o node_config.go --out-name NodeConfig --kind "cilium.io/v2:NodeConfig" --types-pkg github.com/cilium/cilium/pkg/datapath/config/types
Defensive patterns

Strategy: validation

Validate before calling

# Fail early if struct-typed config vars exist but --types-pkg is absent
grep -Eq 'volatile const struct|volatile const union' bpf/*.c \
  && ! echo "$DPGEN_FLAGS" | grep -q -- --types-pkg \
  && { echo "struct-typed config variables require --types-pkg"; exit 1; }

Try / catch

if err := runConfig(cmd, args); err != nil {
	if strings.Contains(err.Error(), "types package is empty") {
		fmt.Fprintln(os.Stderr, "add --types-pkg <go/import/path> to the dpgen config invocation")
		os.Exit(2)
	}
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

Prevention

When it happens

Trigger: Running `dpgen config` on an object whose DECLARE_CONFIG variables include a struct- or union-typed variable while omitting --types-pkg; configOpts.typesPkg is the empty string when btfVarGoTypeName hit a *btf.Struct or *btf.Union case.

Common situations: Someone adds a new struct-typed config variable to the eBPF datapath but doesn't know dpgen now requires the types package; copying an older dpgen invocation line that predates struct-typed config support; wiring dpgen in a build script with hardcoded flags that lack --types-pkg.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/9dc02369277b2077. Report an issue: GitHub.