{"record":{"id":"75d0ee7361bf03a9","repo":"hashicorp/nomad","slug":"timeout-cannot-be-negative","errorCode":null,"errorMessage":"timeout cannot be negative","messagePattern":"timeout cannot be negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/eval_broker.go","lineNumber":148,"sourceCode":"\n// ReadyEvaluations is a list of ready evaluations across multiple jobs. We\n// implement the container/heap interface so that this is a priority queue.\ntype ReadyEvaluations []*structs.Evaluation\n\n// PendingEvaluations is a list of pending evaluations for a given job. We\n// implement the container/heap interface so that this is a priority queue.\ntype PendingEvaluations []*structs.Evaluation\n\n// NewEvalBroker creates a new evaluation broker. This is parameterized\n// with the timeout used for messages that are not acknowledged before we\n// assume a Nack and attempt to redeliver as well as the deliveryLimit\n// which prevents a failing eval from being endlessly delivered. The\n// initialNackDelay is the delay before making a Nacked evaluation available\n// again for the first Nack and subsequentNackDelay is the compounding delay\n// after the first Nack.\nfunc NewEvalBroker(ctx context.Context, timeout, initialNackDelay, subsequentNackDelay time.Duration, deliveryLimit int) (*EvalBroker, error) {\n\tif timeout < 0 {\n\t\treturn nil, fmt.Errorf(\"timeout cannot be negative\")\n\t}\n\tb := &EvalBroker{\n\t\tnackTimeout:          timeout,\n\t\tdeliveryLimit:        deliveryLimit,\n\t\tenabled:              false,\n\t\tenabledNotifier:      broker.NewGenericNotifier(ctx),\n\t\tstats:                new(BrokerStats),\n\t\tevals:                make(map[string]int),\n\t\tjobEvals:             make(map[structs.NamespacedID]string),\n\t\tpending:              make(map[structs.NamespacedID]PendingEvaluations),\n\t\tcancelable:           make([]*structs.Evaluation, 0, structs.MaxUUIDsPerWriteRequest),\n\t\tready:                make(map[string]ReadyEvaluations),\n\t\tunack:                make(map[string]*unackEval),\n\t\twaiting:              make(map[string]chan struct{}),\n\t\trequeue:              make(map[string]*structs.Evaluation),\n\t\ttimeWait:             make(map[string]*time.Timer),\n\t\tinitialNackDelay:     initialNackDelay,\n\t\tsubsequentNackDelay:  subsequentNackDelay,","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/eval_broker.go#L130-L166","documentation":"NewEvalBroker validates that the nack timeout is non-negative before constructing the broker. The nack timeout governs how long a dequeued evaluation can go unacknowledged before being requeued; a negative value is meaningless and is rejected at construction time. This is a programming/configuration error, not a runtime condition.","triggerScenarios":"Calling NewEvalBroker (directly in tests like dummyFSM/testBrokerFromConfig, or via NewServer) with a negative time.Duration for the timeout parameter, typically from a config where TimeoutNack = -1 or an unvalidated user-supplied duration.","commonSituations":"Operator sets a negative value in server config for evaluation nack timeout; a test harness passes -1 to mean 'disabled'; integer/decimal parsing mistakes turning '-1s' into a negative duration.","solutions":["Pass a positive timeout, e.g. time.Minute (the Nomad default is 1 minute).","Validate the server config value before calling NewServer and reject negatives with a clear message.","If the intent is 'no nack timeout', pass 0 or the maximum duration, not a negative value.","Fix parsing of the config value (ensure time.ParseDuration output isn't negated)."],"exampleFix":"// before\nb, err := NewEvalBroker(ctx, -1*time.Second, nackDelay, subDelay, limit)\n// after\ntimeout := 1 * time.Minute\nb, err := NewEvalBroker(ctx, timeout, nackDelay, subDelay, limit)","handlingStrategy":"validation","validationCode":"if nackTimeout < 0 {\n\treturn fmt.Errorf(\"nack timeout must be >= 0, got %s\", nackTimeout)\n}\nbroker, err := NewEvalBroker(ctx, nackTimeout, initDelay, subDelay, limit)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate durations from config with time.ParseDuration and range checks","Never use -1 as a sentinel for 'disabled'; use 0 or math.MaxInt64","Add config linter rules for negative durations"],"tags":["go","nomad","scheduler","configuration","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}