grafana/k6 · error

couldn't parse secret source configuration

Error message

couldn't parse secret source configuration %q

What it means

Each --secret-source entry (or K6_SECRET_SOURCE value) is first split on the first '='. If an entry contains no '=' AND is empty or whitespace-only, there is no source type to look up, so k6 rejects the whole secret configuration at startup. A non-empty type-only entry like 'file' is valid and uses built-in defaults.

Solutions

  1. Remove the empty --secret-source entry from the command or CI environment
  2. Give the flag a real value: --secret-source=file (defaults) or --secret-source=file=path/to/secrets
  3. If building flags dynamically, emit the flag only when the value is non-empty

Example fix

# before
k6 run --secret-source= script.js

# after
k6 run --secret-source=file=secrets.txt script.js
Defensive patterns

Strategy: validation

Validate before calling

# when building --secret-source flags dynamically, drop empty entries
SECRET_FLAGS=()
for src in ${SECRET_SOURCES}; do
  [ -n "$(printf '%s' "$src" | tr -d '[:space:]')" ] && SECRET_FLAGS+=(--secret-source="$src")
done
k6 run "${SECRET_FLAGS[@]}" script.js

Prevention

When it happens

Trigger: `k6 run --secret-source= script.js` (empty flag value), `--secret-source " "`, or a shell loop/CI template that emits `--secret-source=` when its input variable is blank.

Common situations: Dynamically built command lines that append --secret-source=$VAR with VAR unset; YAML/CI matrices leaving the secret-source variable empty; copy-paste leaving a stray empty flag.

Related errors


AI-assisted analysis of grafana/k6@8d06114777 (2026-08-18). Data as JSON: /api/errors/3f24a8d1d9b98d5c. Report an issue: GitHub.

Appendix: source

Thrown at internal/cmd/root.go:504

		pcancel()
	})
}

//nolint:gocognit
func createSecretSources(gs *state.GlobalState) (map[string]secretsource.Source, error) {
	baseParams := secretsource.Params{
		Logger:      gs.Logger,
		Environment: gs.Env,
		FS:          gs.FS,
		Usage:       gs.Usage,
	}

	result := make(map[string]secretsource.Source)
	for _, line := range gs.Flags.SecretSource {
		t, config, ok := strings.Cut(line, "=")
		if !ok {
			if strings.TrimSpace(line) == "" {
				return nil, fmt.Errorf("couldn't parse secret source configuration %q", line)
			}
			// Allow any non-empty source type without explicit config — it will use
			// environment variables and built-in defaults.
			t = line
			config = ""
		}
		secretSources := ext.Get(ext.SecretSourceExtension)
		found, ok := secretSources[t]
		if !ok {
			return nil, fmt.Errorf("no secret source for type %q for configuration %q", t, line)
		}
		c := found.Module.(secretsource.Constructor) //nolint:forcetypeassert
		params := baseParams
		name, isDefault, config := extractNameAndDefault(config)
		if name == "" {
			// Default the name to the source type to avoid an empty-string key.
			name = t
		}

View on GitHub (pinned to 8d06114777)