ory/kratos · error

invalid UUID in --start-after

Error message

invalid UUID in --start-after %q

What it means

parseStartAfter validates that the value part of a "key=uuid" --start-after argument is a valid UUID via uuid.FromString. If parsing fails, the underlying UUID parse error is wrapped with "invalid UUID in --start-after <arg>". Raised before any database work, purely from CLI input validation.

Solutions

  1. Copy the exact UUID from the previous run's resume output or the database id column.
  2. Verify the value is a 36-character hyphenated UUID (8-4-4-4-12).
  3. Quote the argument to prevent shell mangling.

Example fix

// before
kratos migrate normalize-phone --start-after credentials=abc123
// after
kratos migrate normalize-phone --start-after credentials=018f3c2e-3f4a-7b8c-9d0e-1f2a3b4c5d6e
Defensive patterns

Strategy: validation

Validate before calling

re := regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
if !re.MatchString(val) {
    return fmt.Errorf("not a UUID: %q", val)
}

Type guard

func isUUID(s string) bool {
    _, err := uuid.Parse(s)
    return err == nil
}

Try / catch

null

Prevention

When it happens

Trigger: Passing --start-after credentials=not-a-uuid, a truncated UUID, a different ID format (e.g. numeric ID), or an empty value after '='.

Common situations: Hand-copying an ID from logs with typos or truncation, using a non-UUID primary key from a different table, or forgetting the ID entirely after the '=' sign.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/55a0ece1d8cc9648. Report an issue: GitHub.

Appendix: source

Thrown at cmd/migrate/normalize_phone_handler.go:134

type tableConfig struct {
	key         string
	name        string
	selectQuery string
	updateQuery string
}

// parseStartAfter parses --start-after flags of the form "key=uuid".
func parseStartAfter(args []string) (map[string]uuid.UUID, error) {
	result := make(map[string]uuid.UUID)
	for _, arg := range args {
		key, val, ok := strings.Cut(arg, "=")
		if !ok {
			return nil, errors.Errorf("invalid --start-after format %q: expected key=uuid (e.g. credentials=<id>)", arg)
		}
		id, err := uuid.FromString(val)
		if err != nil {
			return nil, errors.Wrapf(err, "invalid UUID in --start-after %q", arg)
		}
		result[key] = id
	}
	return result, nil
}

func printSummary(cmd *cobra.Command, tables []tableConfig, allStats []normalizeStats) {
	out := cmd.OutOrStdout()

	_, _ = fmt.Fprintln(out)

	tw := tabwriter.NewWriter(out, 0, 0, 2, ' ', 0)
	_, _ = fmt.Fprintln(tw, "\tSCANNED\tUPDATED\tSKIPPED\tERRORS")

	var total normalizeStats
	for i, table := range tables {
		s := allStats[i]
		_, _ = fmt.Fprintf(tw, "%s\t%d\t%d\t%d\t%d\n", table.name, s.scanned, s.updated, s.skipped, s.errors)

View on GitHub (pinned to b86338da04)