argoproj/argo-workflows · error
AlreadyExists
AlreadyExists
Error message
sync limit cannot be created as it already exists
What it means
createSyncLimit refuses to create a sync limit when the target configmap already contains the requested key; existing limits must be updated instead. It fetches the configmap first and checks cm.Data[req.Key].
Source
Thrown at server/sync/sync_cm.go:40
func (s *configMapSyncProvider) createSyncLimit(ctx context.Context, req *syncpkg.CreateSyncLimitRequest) (*syncpkg.SyncLimitResponse, error) {
if req.Limit <= 0 {
return nil, sutils.ToStatusError(fmt.Errorf("limit must be greater than zero"), codes.InvalidArgument)
}
if err := checkConfigMapPermission(ctx, "create", req.Namespace); err != nil {
return nil, err
}
kubeClient := auth.GetKubeClient(ctx)
configmapGetter := kubeClient.CoreV1().ConfigMaps(req.Namespace)
cm, err := configmapGetter.Get(ctx, req.CmName, metav1.GetOptions{})
if err == nil {
_, has := cm.Data[req.Key]
if has {
return nil, sutils.ToStatusError(fmt.Errorf("sync limit cannot be created as it already exists"), codes.AlreadyExists)
}
return s.handleUpdateSyncLimit(ctx, &syncpkg.UpdateSyncLimitRequest{
CmName: req.CmName,
Namespace: req.Namespace,
Key: req.Key,
Limit: req.Limit,
Type: syncpkg.SyncConfigType_CONFIGMAP,
}, false)
}
cm = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: req.CmName,
Namespace: req.Namespace,
},
Data: map[string]string{
req.Key: fmt.Sprint(req.Limit),
},View on GitHub (pinned to 35bff19146)
Solutions
- Call UpdateSyncLimit instead of CreateSyncLimit to change the existing key's value
- Delete the key from the configmap (or use DeleteSyncLimit) before re-creating
- Make provisioning idempotent: on AlreadyExists, fall back to update
Example fix
// before
_, err := client.CreateSyncLimit(ctx, req)
// after
_, err := client.CreateSyncLimit(ctx, req)
if st, ok := status.FromError(err); ok && st.Code() == codes.AlreadyExists {
_, err = client.UpdateSyncLimit(ctx, &syncpkg.UpdateSyncLimitRequest{CmName: req.CmName, Namespace: req.Namespace, Key: req.Key, Limit: req.Limit})
} Defensive patterns
Strategy: try-catch
Validate before calling
cm, err := kube.CoreV1().ConfigMaps(ns).Get(ctx, name, metav1.GetOptions{})
if err == nil {
if _, exists := cm.Data[key]; exists { /* use update instead of create */ }
} Try / catch
_, err := client.CreateSyncLimit(ctx, req)
if st, ok := status.FromError(err); ok && st.Code() == codes.AlreadyExists {
_, err = client.UpdateSyncLimit(ctx, toUpdateReq(req))
} Prevention
- Make limit provisioning idempotent (create-or-update)
- Check the configmap key before creating
- Serialize provisioning scripts to avoid duplicate creators
When it happens
Trigger: CreateSyncLimit targeting a configmap/key that already holds a limit value; note Get errors other than NotFound fall through to update semantics, but an existing key short-circuits with AlreadyExists.
Common situations: Re-running a bootstrap script that creates limits, two operators provisioning the same semaphore key, retrying a create after a partial success.
Related errors
- InvalidArgument
- NotFound
- InvalidArgument
- failed to update ConfigMap %s: %w
- memoization configmap doesn't have %s label, refusing to use
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/72d1e8b6dba765f1.
Report an issue: GitHub.