grafana/k6 · error

missing or empty type field

Error message

missing or empty type field

What it means

Emitted by BaseConfig.Validate (lib/executor/base_config.go:62-64) when the scenario's executor/type field is empty. The type (JSON key 'executor', line 29) selects the execution strategy (constant-vus, ramping-vus, constant-arrival-rate, ramping-arrival-rate, shared-iterations, per-vu-iterations); k6 cannot instantiate an executor without it. Note Validate() only reports a missing type at this base level — an unknown type string fails earlier during unmarshaling in lib/executor.Decode.

Source

Thrown at lib/executor/base_config.go:63

		GracefulStop: types.NewNullDuration(DefaultGracefulStopValue, false),
	}
}

// Validate checks some basic things like present name, type, and a positive start time
func (bc BaseConfig) Validate() (result []error) {
	// Some just-in-case checks, since those things are likely checked in other places or
	// even assigned by us:
	if bc.Name == "" {
		result = append(result, errors.New("scenario name can't be empty"))
	}
	if !scenarioNameWhitelist.MatchString(bc.Name) {
		result = append(result, errors.New(scenarioNameErr))
	}
	if bc.Exec.Valid && bc.Exec.String == "" {
		result = append(result, errors.New("exec value cannot be empty"))
	}
	if bc.Type == "" {
		result = append(result, errors.New("missing or empty type field"))
	}
	// The actually reasonable checks:
	if bc.StartTime.Duration < 0 {
		result = append(result, errors.New("the startTime can't be negative"))
	}
	if bc.GracefulStop.Duration < 0 {
		result = append(result, errors.New("the gracefulStop timeout can't be negative"))
	}
	return result
}

// GetName returns the name of the scenario.
func (bc BaseConfig) GetName() string {
	return bc.Name
}

// GetType returns the executor's type as a string ID.
func (bc BaseConfig) GetType() string {

View on GitHub (pinned to 93accf6570)

Solutions

  1. Add an explicit executor field, e.g. executor: 'constant-vus' with vus and duration
  2. Pick the executor matching intent: shared-iterations, per-vu-iterations, constant-arrival-rate, ramping-arrival-rate, constant-vus, ramping-vus

Example fix

// before
export const options = {
  scenarios: {
    smoke: { vus: 1, duration: '10s' },
  },
};

// after
export const options = {
  scenarios: {
    smoke: { executor: 'constant-vus', vus: 1, duration: '10s' },
  },
};
Defensive patterns

Strategy: validation

Validate before calling

const VALID = new Set(['shared-iterations','per-vu-iterations','constant-vus','ramping-vus','constant-arrival-rate','ramping-arrival-rate']);
for (const [name, cfg] of Object.entries(options.scenarios || {})) {
  if (!VALID.has(cfg.executor)) throw new Error(`scenario ${name}: missing/unknown executor`);
}

Prevention

When it happens

Trigger: Defining options.scenarios = { smoke: { vus: 1, duration: '10s' } } without an executor key, or programmatically building a BaseConfig with Type left as its zero value and calling Validate().

Common situations: Copy-paste of a scenario block that loses the executor line; assuming duration+vus alone imply constant-vus (they do not in the scenarios API); typos like 'executor' vs 'execution' are instead rejected at JSON decode with a different error.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/92815be59d4ddc96. Report an issue: GitHub.