cilium/cilium · error
Both %T.Transform and .TransformMany cannot be set
Error message
Both %T.Transform and .TransformMany cannot be set
What it means
validate() rejects a ReflectorConfig that sets both Transform and TransformMany, since the reflector cannot know which mapping to apply to each watch event. Exactly one (or neither) must be provided for RegisterReflector to succeed.
Source
Thrown at pkg/k8s/statedb.go:373
func (cfg ReflectorConfig[Obj]) validate() error {
if cfg.Name == "" {
return fmt.Errorf("%T.Name cannot be empty", cfg)
}
if cfg.Table == nil {
return fmt.Errorf("%T.Table cannot be nil", cfg)
}
if (cfg.ListerWatcher == nil) == (cfg.SharedListerWatcher == nil) {
return fmt.Errorf("Exactly one of %[1]T.ListerWatcher and %[1]T.SharedListerWatcher must be set", cfg)
}
if cfg.BufferSize <= 0 {
return fmt.Errorf("%T.BufferSize (%d) must be larger than zero", cfg, cfg.BufferSize)
}
if cfg.BufferWaitTime <= 0 {
return fmt.Errorf("%T.BufferWaitTime (%d) must be larger than zero", cfg, cfg.BufferWaitTime)
}
if cfg.Transform != nil && cfg.TransformMany != nil {
return fmt.Errorf("Both %T.Transform and .TransformMany cannot be set", cfg)
}
return nil
}
type k8sReflector[Obj any] struct {
ReflectorConfig[Obj]
log *slog.Logger
initDone func(statedb.WriteTxn)
db *statedb.DB
table statedb.RWTable[Obj]
source stream.Observable[CacheStoreEvent]
}
func (r *k8sReflector[Obj]) run(ctx context.Context, health cell.Health) error {
if r.CRDSync != nil {
// Wait for the CRD to be registered.
health.OK("Waiting for CRD registration")View on GitHub (pinned to ac7b90affa)
Solutions
- Keep only TransformMany and set Transform to nil (or the reverse) before calling RegisterReflector
- Refactor config construction so a single transform strategy is selected via a flag/branch
- Centralize transform assignment in one helper to avoid double assignment
Example fix
// before
cfg := k8s.ReflectorConfig[Obj]{..., Transform: t, TransformMany: tm}
// after
cfg := k8s.ReflectorConfig[Obj]{..., TransformMany: tm} // Transform cleared Defensive patterns
Strategy: validation
Validate before calling
if cfg.Transform != nil && cfg.TransformMany != nil {
return fmt.Errorf("reflector %s: set either Transform or TransformMany, not both", cfg.Name)
} Type guard
func hasSingleTransform[Obj statedb.Object](cfg k8s.ReflectorConfig[Obj]) bool {
return !(cfg.Transform != nil && cfg.TransformMany != nil)
} Try / catch
if err := RegisterReflector(db, cfg); err != nil {
if strings.Contains(err.Error(), "Transform") {
return fmt.Errorf("reflector %s: conflicting transform config: %w", cfg.Name, err)
}
return err
} Prevention
- Choose one transform strategy per reflector and encode it in the config builder
- When switching to TransformMany, delete the Transform assignment in the same commit
- Add a table-driven test covering the valid combinations of transform fields
When it happens
Trigger: RegisterReflector with a config where both Transform and TransformMany function fields are non-nil — typically after switching from per-object to per-batch transforms (or vice versa) without clearing the old field.
Common situations: Migrating to TransformMany for efficiency while leaving the legacy Transform assigned; a config builder that unconditionally sets Transform and additionally sets TransformMany for list handling.
Related errors
- %T.Table cannot be nil
- Exactly one of %[1]T.ListerWatcher and %[1]T.SharedListerWat
- %T.BufferSize (%d) must be larger than zero
- %T.BufferWaitTime (%d) must be larger than zero
- peer %s does not have a PeerASN
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/e62543a1b6106c36.
Report an issue: GitHub.