gotify/server · error

sort key is not unique

Error message

sort key is not unique

What it means

handleApplicationError maps gorm.ErrDuplicatedKey to this 400 message. It is returned when an INSERT/UPDATE violates a unique constraint — in this codebase, the applications table enforces uniqueness on the sort key column, so two applications cannot share the same sort key.

Source

Thrown at api/application.go:563

		name := gen()
		if !exist(imgDir + name) {
			return name
		}
	}
}

func ValidApplicationImageExt(ext string) bool {
	switch strings.ToLower(ext) {
	case ".gif", ".png", ".jpg", ".jpeg":
		return true
	default:
		return false
	}
}

func handleApplicationError(ctx *gin.Context, err error) {
	if errors.Is(err, gorm.ErrDuplicatedKey) {
		ctx.AbortWithError(400, errors.New("sort key is not unique"))
	} else {
		ctx.AbortWithError(500, err)
	}
}

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Send a unique sort key (or omit it if the API can default/append to the end)
  2. Query existing sort keys first and pick max+1 (or a gap) before creating
  3. Retry with a different key if racing with concurrent creations
  4. Add server-side logic to auto-assign the next free sort key instead of trusting client input

Example fix

// before
POST /applications {"name":"App","sort_key":1}   // 1 already used
// after
const max = Math.max(...apps.map(a => a.sort_key), 0);
POST /applications {"name":"App","sort_key": max + 1}
Defensive patterns

Strategy: validation

Validate before calling

const keys = apps.map(a => a.sort_key);
const nextKey = Math.max(0, ...keys) + 1;
await api.post('/applications', {name, sort_key: nextKey});

Try / catch

try {
  await api.post('/applications', payload);
} catch (e) {
  if (e.response?.status === 400 && e.response?.data?.includes('sort key is not unique')) {
    payload.sort_key = Date.now(); // or recompute a free key and retry once
  } else throw e;
}

Prevention

When it happens

Trigger: CreateApplication called with a SortKey value that already exists on another application row (unique index hit, GORM translates the driver's duplicate-key error to gorm.ErrDuplicatedKey).

Common situations: Clients hardcoding sort values like 0 or 1 for every new application; concurrent requests picking the same sort key; imports/migrations that reuse existing sort keys; after a unique index was newly added and legacy rows already collide.

Related errors


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/aa2dedd3385637af. Report an issue: GitHub.