micro/go-micro · error

cannot upsert record ${key}

Error message

cannot upsert record ${key}

What it means

The pgx store's Write method executes an upsert (INSERT ... ON CONFLICT) for the record key, with an expiry timestamp when r.Expiry is set and NULL otherwise, and wraps any failure with 'cannot upsert record <key>'. It means the database rejected or failed the INSERT/UPSERT statement for that specific key.

Source

Thrown at store/postgres/pgx/pgx.go:349

	}

	db, queries, err := s.db(options.Database, options.Table)
	if err != nil {
		return err
	}

	metadata := make(Metadata)
	for k, v := range r.Metadata {
		metadata[k] = v
	}

	if r.Expiry != 0 {
		_, err = db.Exec(s.options.Context, queries.Write, r.Key, r.Value, metadata, time.Now().Add(r.Expiry))
	} else {
		_, err = db.Exec(s.options.Context, queries.Write, r.Key, r.Value, metadata, nil)
	}
	if err != nil {
		return errors.Wrap(err, "cannot upsert record "+r.Key)
	}

	return nil
}

// Delete records with keys
func (s *sqlStore) Delete(key string, opts ...store.DeleteOption) error {
	var options store.DeleteOptions
	for _, o := range opts {
		o(&options)
	}

	db, queries, err := s.db(options.Database, options.Table)
	if err != nil {
		return err
	}

	_, err = db.Exec(s.options.Context, queries.Delete, key)

View on GitHub (pinned to 24529f1404)

Solutions

  1. Check the wrapped PostgreSQL error for the precise cause (connection vs constraint vs value size).
  2. Verify connectivity and that the table still exists (re-run initialization if dropped).
  3. Retry the write on transient errors (connection reset, serialization failure).
  4. Validate the key and value sizes against column definitions and server limits.
  5. Confirm the expiry timestamp is representable (avoid extreme durations overflowing timestamp).

Example fix

// before
err := store.Write(&store.Record{Key: key, Value: val, Expiry: 1<<62}) // overflow-like huge expiry
// after
exp := time.Hour
if r.Expiry != 0 && r.Expiry < 24*time.Hour*365 {
	exp = r.Expiry
}
err := store.Write(&store.Record{Key: key, Value: val, Expiry: exp})
Defensive patterns

Strategy: retry

Validate before calling

// before write
if r.Key == "" || len(r.Value) > maxValueSize { return ErrInvalidRecord }
if r.Expiry < 0 || r.Expiry > maxExpiry { return ErrInvalidExpiry }
if err := db.Ping(ctx); err != nil { // reconnect pool first }

Type guard

func isWriteErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "cannot upsert record")
}

Try / catch

err := store.Write(rec)
if err != nil {
	var pgErr *pgconn.PgError
	if errors.As(err, &pgErr) && transientCodes[pgErr.Code] { // e.g. 08006, 40001
		return retryWithBackoff(func() error { return store.Write(rec) })
	}
	return err
}

Prevention

When it happens

Trigger: Calling store.Write (or table.Write) with a record whose key/value/metadata cannot be written: connection failure, unique constraint conflict not covered by the ON CONFLICT clause, value too large, expired statement timeout, or invalid key characters breaking parameterization assumptions.

Common situations: Postgres connection pool exhausted or server restarted mid-write; writing records with expiry where the server clock/timezone handling mismatches; writing very large values exceeding column or packet limits; table dropped externally while the app runs.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/c176479f8924dc62. Report an issue: GitHub.