gofr-dev/gofr · error

failed to record migration: %w

Error message

failed to record migration: %w

What it means

Thrown when IndexDocument fails while persisting a migration record (document ID = migration number for idempotency) into the gofr_migrations index during commitMigration. The migration itself ran, but its record could not be written, so subsequent runs may re-execute it. The wrapped error holds the actual ES failure.

Source

Thrown at pkg/gofr/migration/elasticsearch.go:164

	return em.migrator.beginTransaction(c)
}

// commitMigration records the migration in the tracking index.
func (em elasticsearchMigrator) commitMigration(c *container.Container, data transactionData) error {
	if data.UsedDatasources[dsElasticsearch] {
		migrationDoc := map[string]any{
			"version":    data.MigrationNumber,
			"method":     "UP",
			"start_time": data.StartTime.Format(time.RFC3339),
			"duration":   time.Since(data.StartTime).Milliseconds(),
		}

		// Use the migration number as the document ID for idempotency
		docID := fmt.Sprintf("%d", data.MigrationNumber)

		err := c.Elasticsearch.IndexDocument(context.Background(), elasticsearchMigrationIndex, docID, migrationDoc)
		if err != nil {
			return fmt.Errorf("failed to record migration: %w", err)
		}

		c.Debugf("Inserted record for migration %v in Elasticsearch gofr_migrations index", data.MigrationNumber)
	}

	return em.migrator.commitMigration(c, data)
}

// rollback is a no-op for Elasticsearch migrations.
func (em elasticsearchMigrator) rollback(c *container.Container, data transactionData) {
	em.migrator.rollback(c, data)
	c.Fatalf("Migration %v failed.", data.MigrationNumber)
}

func (em elasticsearchMigrator) lock(ctx context.Context, cancel context.CancelFunc, c *container.Container, ownerID string) error {
	return em.migrator.lock(ctx, cancel, c, ownerID)
}

View on GitHub (pinned to 187eb24962)

Solutions

  1. Check the wrapped ES error for the specific cause (mapping/permissions/cluster state)
  2. Ensure the gofr_migrations index allows writes and has a compatible mapping
  3. Restore ES availability, then re-run the migration — doc ID idempotency makes it safe
  4. Clear disk-watermark read-only blocks if the index is read-only

Example fix

// before
// commit fails: failed to record migration: version_conflict_engine_exception
// after
// verify doc for this migration number isn't already applied, then retry commit
_, err := c.Elasticsearch.GetDocument(ctx, "gofr_migrations", fmt.Sprintf("%d", data.MigrationNumber))
if err != nil { return em.commitMigration(c, data) } // idempotent retry
Defensive patterns

Strategy: retry

Validate before calling

// pre-check write access
if err := c.Elasticsearch.IndexDocument(ctx, "gofr_migrations", "_healthcheck", map[string]string{"ping":"1"}); err != nil {
    return fmt.Errorf("gofr_migrations index not writable: %w", err)
}

Try / catch

err := migrator.Run(c)
if err != nil && strings.Contains(err.Error(), "failed to record migration") {
    // document ID is idempotent: safe to retry with backoff
    time.Sleep(2 * time.Second)
    err = migrator.Run(c)
}

Prevention

When it happens

Trigger: c.Elasticsearch.IndexDocument() returns an error for the migration doc: ES down, mapping conflict on the index, document too large, write/403 permission, or version conflict on the same doc ID.

Common situations: Index created earlier with an incompatible mapping; read-only cluster due to disk watermark; network blip mid-migration; ES credentials lacking write access.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/9261d3512a029cef. Report an issue: GitHub.