argoproj/argo-workflows · error
InvalidArgument
InvalidArgument
Error message
limit must be greater than zero
What it means
The configmap-backed sync-limit gRPC API rejects CreateSyncLimit requests whose Limit field is zero or negative. Limits model semaphore slot counts and must be positive integers. The check runs before permission and configmap access.
Source
Thrown at server/sync/sync_cm.go:25
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
syncpkg "github.com/argoproj/argo-workflows/v4/pkg/apiclient/sync"
"github.com/argoproj/argo-workflows/v4/server/auth"
sutils "github.com/argoproj/argo-workflows/v4/server/utils"
authutil "github.com/argoproj/argo-workflows/v4/util/auth"
)
type configMapSyncProvider struct{}
var _ ConfigProvider = &configMapSyncProvider{}
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,View on GitHub (pinned to 35bff19146)
Solutions
- Pass a positive integer limit (>= 1) in the request
- Check the calling client/UI is not sending the zero-value default
- Use UpdateSyncLimit/DeleteSyncLimit semantics intentionally rather than limit 0
Example fix
// before
req := &syncpkg.CreateSyncLimitRequest{CmName: "my-cm", Key: "k", Limit: 0}
// after
req := &syncpkg.CreateSyncLimitRequest{CmName: "my-cm", Key: "k", Limit: 3} Defensive patterns
Strategy: validation
Validate before calling
if limit < 1 { return fmt.Errorf("CreateSyncLimit requires limit >= 1, got %d", limit) } Type guard
func validLimit(n int32) bool { return n > 0 } Try / catch
_, err := syncClient.CreateSyncLimit(ctx, req)
if st, ok := status.FromError(err); ok && st.Code() == codes.InvalidArgument {
return fmt.Errorf("bad request: %v", st.Message())
} Prevention
- Never rely on protobuf zero-value ints for required fields
- Clamp computed semaphore sizes to a minimum of 1
- Validate limit input in CLIs/UIs before calling the API
When it happens
Trigger: Calling the SyncService.CreateSyncLimit gRPC/CLI with limit: 0 or a negative value in the CreateSyncLimitRequest.
Common situations: Default-zero protobuf int32 left unset by clients, UI sending 0 for 'unset', or computed limits evaluating to 0.
Related errors
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/ab61333d0a8aac0b.
Report an issue: GitHub.