{"record":{"id":"746ec0cdd946ea67","repo":"grafana/k6","slug":"stage-d-doesn-t-have-a-specified-duration","errorCode":null,"errorMessage":"stage %d doesn't have a specified duration","messagePattern":"stage (.+?) doesn't have a specified duration","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/options.go","lineNumber":117,"sourceCode":"\t\tThrow:                   getNullBool(flags, \"throw\"),\n\t\tDiscardResponseBodies:   getNullBool(flags, \"discard-response-bodies\"),\n\t\tMetricSamplesBufferSize: null.NewInt(1000, false),\n\t}\n\n\t// Using Changed() because GetStringSlice() doesn't differentiate between empty and no value\n\tif flags.Changed(\"stage\") {\n\t\tstageStrings, err := flags.GetStringSlice(\"stage\")\n\t\tif err != nil {\n\t\t\treturn opts, err\n\t\t}\n\t\topts.Stages = []lib.Stage{}\n\t\tfor i, s := range stageStrings {\n\t\t\tvar stage lib.Stage\n\t\t\tif err := stage.UnmarshalText([]byte(s)); err != nil {\n\t\t\t\treturn opts, fmt.Errorf(\"error for stage %d: %w\", i, err)\n\t\t\t}\n\t\t\tif !stage.Duration.Valid {\n\t\t\t\treturn opts, fmt.Errorf(\"stage %d doesn't have a specified duration\", i)\n\t\t\t}\n\t\t\topts.Stages = append(opts.Stages, stage)\n\t\t}\n\t}\n\n\tif flags.Changed(\"execution-segment\") {\n\t\texecutionSegmentStr, err := flags.GetString(\"execution-segment\")\n\t\tif err != nil {\n\t\t\treturn opts, err\n\t\t}\n\t\tsegment := new(lib.ExecutionSegment)\n\t\terr = segment.UnmarshalText([]byte(executionSegmentStr))\n\t\tif err != nil {\n\t\t\treturn opts, err\n\t\t}\n\t\topts.ExecutionSegment = segment\n\t}\n","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cmd/options.go#L99-L135","documentation":"Companion check in the same --stage loop: the stage string parsed successfully but produced no duration (empty left side of ':'), which happens with a target-only stage like `--stage :10`. lib.Stage.UnmarshalText happily decodes target-only stages (duration is optional at the text level), but the CLI requires every --stage to carry an explicit duration, so the %d-indexed stage is rejected here.","triggerScenarios":"`k6 run --stage :100 script.js` (intended 'ramp straight to 100 VUs' but no duration given); `--stage :` (both sides empty after shell quoting mishaps); trailing empty elements from `--stage 1m:5,` style splitting.","commonSituations":"Modeling instant VU jumps and forgetting k6 expresses them as short durations (e.g. `1s:100`); copy-paste from JSON ramping-vus options where duration defaults existed elsewhere; typos deleting the duration part.","solutions":["Always prefix a duration: `--stage 1s:100` for a near-instant jump to 100 VUs","Check the flagged index (0-based) among your --stage occurrences and add the missing 'Nm:' prefix","For plateau-then-drop patterns, spell each stage explicitly: `--stage 1m:100 --stage 1m:100`","Move complex ramps into the script's options.stages where each entry requires duration by schema and errors are clearer"],"exampleFix":"# before\nk6 run --stage :100 script.js\n# error: stage 0 doesn't have a specified duration\n\n# after\nk6 run --stage 1s:100 script.js","handlingStrategy":"validation","validationCode":"# Require a non-empty duration on the left of ':' in every --stage\nfor s in \"${stages[@]}\"; do\n  [[ \"$s\" =~ ^.+:[0-9]*$ && ! \"$s\" =~ ^:[0-9]*$ ]] || { echo \"stage '$s' needs a duration (e.g. 1s:100)\"; exit 1; }\ndone","typeGuard":"function stageHasDuration(s) {\n  const left = s.split(\":\")[0];\n  return left.trim() !== \"\" && /^[0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h)$/.test(left.trim());\n}","tryCatchPattern":null,"preventionTips":["Express instant VU jumps as a short duration: 1s:100, never :100","Build stage strings from {duration,target} pairs in code so the duration field can't be dropped","Validate the full stage list in one place before passing it to k6"],"tags":["cli","options","stages","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}