{"record":{"id":"4b08f7c0c853a13a","repo":"grafana/k6","slug":"the-starttime-can-t-be-negative","errorCode":null,"errorMessage":"the startTime can't be negative","messagePattern":"the startTime can't be negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/executor/base_config.go","lineNumber":67,"sourceCode":"// Validate checks some basic things like present name, type, and a positive start time\nfunc (bc BaseConfig) Validate() (result []error) {\n\t// Some just-in-case checks, since those things are likely checked in other places or\n\t// even assigned by us:\n\tif bc.Name == \"\" {\n\t\tresult = append(result, errors.New(\"scenario name can't be empty\"))\n\t}\n\tif !scenarioNameWhitelist.MatchString(bc.Name) {\n\t\tresult = append(result, errors.New(scenarioNameErr))\n\t}\n\tif bc.Exec.Valid && bc.Exec.String == \"\" {\n\t\tresult = append(result, errors.New(\"exec value cannot be empty\"))\n\t}\n\tif bc.Type == \"\" {\n\t\tresult = append(result, errors.New(\"missing or empty type field\"))\n\t}\n\t// The actually reasonable checks:\n\tif bc.StartTime.Duration < 0 {\n\t\tresult = append(result, errors.New(\"the startTime can't be negative\"))\n\t}\n\tif bc.GracefulStop.Duration < 0 {\n\t\tresult = append(result, errors.New(\"the gracefulStop timeout can't be negative\"))\n\t}\n\treturn result\n}\n\n// GetName returns the name of the scenario.\nfunc (bc BaseConfig) GetName() string {\n\treturn bc.Name\n}\n\n// GetType returns the executor's type as a string ID.\nfunc (bc BaseConfig) GetType() string {\n\treturn bc.Type\n}\n\n// GetStartTime returns the starting time, relative to the beginning of the","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/lib/executor/base_config.go#L49-L85","documentation":"Emitted by BaseConfig.Validate (lib/executor/base_config.go:66-68) when a scenario's startTime is a negative duration. startTime (types.NullDuration) delays when the scenario begins relative to test start; negative values would mean starting before the test exists, so they are rejected. The check only fires when the value is non-zero, since the zero NullDuration default is skipped via the Valid/zero semantics of the surrounding code (negative values always have Duration < 0).","triggerScenarios":"Setting scenarios: { late: { executor: 'constant-vus', startTime: '-5s', ... } }, or unmarshaling a config/JSON where startTime parses to a negative duration (e.g. \"-1m30s\").","commonSituations":"Offsets computed from variables that go negative (e.g. startTime based on a computed delay), or a sign typo when hand-editing configs. Rare in practice because k6 duration parsing makes '-5s' an explicit choice.","solutions":["Use a positive or zero startTime, e.g. startTime: '5s' to delay the scenario","If the intent was to run immediately, remove the startTime property"],"exampleFix":"// before\nexport const options = {\n  scenarios: { late: { executor: 'constant-vus', startTime: '-5s', vus: 2, duration: '1m' } },\n};\n\n// after\nexport const options = {\n  scenarios: { late: { executor: 'constant-vus', startTime: '5s', vus: 2, duration: '1m' } },\n};","handlingStrategy":"validation","validationCode":"// JS: guard computed startTime values\nconst delay = Number(__ENV.SCENARIO_DELAY_S || 0);\nif (delay < 0) throw new Error('scenario delay must be >= 0');\nexport const options = { scenarios: { late: { executor: 'constant-vus', startTime: `${delay}s`, vus: 1, duration: '1m' } } };","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat startTime as a non-negative delay; compute it with Math.max(0, value) when derived from variables","Prefer Go/JS duration literals with explicit positive signs in generated configs"],"tags":["config","scenarios","starttime","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}