ory/kratos · error

normalizing

Error message

normalizing %s

What it means

NormalizePhoneNumbers iterates over tables needing phone normalization (identity_credentials etc.) and calls normalizeTable per table. If batch normalization of a table fails at any step, the error is wrapped with "normalizing <table>". It identifies which table's processing failed; the underlying cause carries the real failure.

Solutions

  1. Check the wrapped cause to see whether it was the query or the update phase.
  2. Re-run the command; it is resumable via --start-after credentials=<last-id>.
  3. Run with --dry-run first to detect data problems before writing.
  4. Resolve database locks/connectivity issues, then retry the affected table.

Example fix

// before: failed mid-run
kratos migrate normalize-phone
// after: resume after the last processed ID
kratos migrate normalize-phone --start-after credentials=018f3c2e-... 
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

if err := normalizePhoneNumbers(cmd, args); err != nil {
    if strings.HasPrefix(err.Error(), "normalizing ") {
        // table-level failure: log table name and resume later
        log.Printf("normalization failed: %v; resume with --start-after", err)
        return cmdx.FailSilently(cmd)
    }
    return err
}

Prevention

When it happens

Trigger: Running the phone-normalization migration command when normalizeTable returns an error for a given table — e.g. failed row query, failed update during a batch, or a write conflict mid-run.

Common situations: Database connection dropped mid-batch, a concurrent process locking rows, or resuming an interrupted run with a stale --start-after ID pointing past deleted rows.

Related errors


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

Appendix: source

Thrown at cmd/migrate/normalize_phone_handler.go:107

			ORDER BY id ASC LIMIT ?`,
			updateQuery: `UPDATE identity_recovery_addresses SET value = ?, updated_at = ? WHERE id = ? AND value = ?`,
		},
	}

	startAfterMap, err := parseStartAfter(flagx.MustGetStringSlice(cmd, "start-after"))
	if err != nil {
		return err
	}

	allStats := make([]normalizeStats, len(tables))
	for i, table := range tables {
		startAfter := startAfterMap[table.key]
		if startAfter != uuid.Nil {
			_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s: resuming after ID %s\n", table.name, startAfter)
		}
		stats, err := normalizeTable(conn, batchSize, batchDelay, startAfter, dryRun, cmd, table)
		if err != nil {
			return errors.Wrapf(err, "normalizing %s", table.name)
		}
		allStats[i] = stats
	}

	printSummary(cmd, tables, allStats)

	return nil
}

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) {

View on GitHub (pinned to b86338da04)