zitadel/zitadel · error

unable to read steps: %w

Error message

unable to read steps: %w

What it means

This error wraps a failure from the mapstructure/viper decoding step in cmd/setup/config.go's readSteps, which unmarshals the YAML config's Steps section into the migration Steps struct. ZITADEL throws it when the setup steps configuration cannot be decoded into the strongly-typed steps object. It indicates the setup command's config for migration steps is malformed or has incompatible types.

Source

Thrown at cmd/setup/config.go:230

		v.SetConfigFile(file)
		err := v.MergeInConfig()
		logging.OnError(ctx, err).Warn("unable to read setup file", "file", file)
	}

	steps := new(Steps)
	err = v.Unmarshal(steps,
		viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
			hook.Base64ToBytesHookFunc(),
			hook.TagToLanguageHookFunc(),
			hook.StringToURLHookFunc(),
			mapstructure.StringToTimeDurationHookFunc(),
			mapstructure.StringToTimeHookFunc(time.RFC3339),
			mapstructure.StringToSliceHookFunc(","),
			mapstructure.TextUnmarshallerHookFunc(),
		)),
	)
	if err != nil {
		return nil, fmt.Errorf("unable to read steps: %w", err)
	}
	return steps, nil
}

View on GitHub (pinned to 13948f2bcd)

Solutions

  1. Check the wrapped error (%w) for the exact mapstructure field that failed to decode.
  2. Fix the type of the offending field in your config YAML / env override to match the Steps struct in cmd/setup/config.go.
  3. Ensure timestamp-like step fields use valid RFC3339 values and list fields are comma-separated strings.
  4. If running an upgraded version, remove stale step keys from your custom config and rely on defaults.

Example fix

// before (config.yaml)
Steps:
  FirstInstancePat: 12345   # wrong type: string expected
// after
Steps:
  FirstInstancePat: "pat12345"
Defensive patterns

Strategy: validation

Validate before calling

// Validate steps config fields' types before running setup:
// e.g. ensure string fields are strings and time fields parse as RFC3339
if _, err := time.Parse(time.RFC3339, cfg.Steps.SomeTimeField); err != nil {
    return fmt.Errorf("invalid Steps.SomeTimeField: %w", err)
}

Try / catch

if _, err := readSteps(v); err != nil {
    var decodeErr *mapstructure.Error
    if errors.As(err, &decodeErr) { /* inspect failing fields */ }
    return err
}

Prevention

When it happens

Trigger: Running `zitadel setup` (or start with steps config) when config.ReadConfig/decoder fails: a YAML key in the Steps section has the wrong type (e.g. string where int expected), an unsupported TextUnmarshaller target value, or the underlying config read returns an error.

Common situations: Hand-edited defaults.yaml/config.yaml with a typo in a steps field type; passing --steps or step config via env vars with values that fail mapstructure hooks (invalid RFC3339 timestamps, wrong comma-separated list); upgrading ZITADEL and an old steps key no longer matches the struct.

Related errors


AI-assisted analysis of zitadel/zitadel@13948f2bcd (2026-09-06). Data as JSON: /api/errors/6a8910b4eb5aa75b. Report an issue: GitHub.