{"record":{"id":"22b91586a8687dab","repo":"netbirdio/netbird","slug":"expiration-must-be-a-positive-duration-e-g-72-22b915","errorCode":null,"errorMessage":"--expiration must be a positive duration (e.g., 720h, 365d, 8760h)","messagePattern":"--expiration must be a positive duration \\(e\\.g\\., 720h, 365d, 8760h\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/rootkey.go","lineNumber":27,"sourceCode":"\n\t\"github.com/netbirdio/netbird/client/internal/updater/reposign\"\n)\n\nvar (\n\tprivKeyFile    string\n\tpubKeyFile     string\n\trootExpiration time.Duration\n)\n\nvar createRootKeyCmd = &cobra.Command{\n\tUse:          \"create-root-key\",\n\tShort:        \"Create a new root key pair\",\n\tLong:         `Create a new root key pair and specify an expiration time for it.`,\n\tSilenceUsage: true,\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t// Validate expiration\n\t\tif rootExpiration <= 0 {\n\t\t\treturn fmt.Errorf(\"--expiration must be a positive duration (e.g., 720h, 365d, 8760h)\")\n\t\t}\n\n\t\t// Run main logic\n\t\tif err := handleGenerateRootKey(cmd, privKeyFile, pubKeyFile, rootExpiration); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to generate root key: %w\", err)\n\t\t}\n\t\treturn nil\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(createRootKeyCmd)\n\tcreateRootKeyCmd.Flags().StringVar(&privKeyFile, \"priv-key-file\", \"\", \"Path to output private key file\")\n\tcreateRootKeyCmd.Flags().StringVar(&pubKeyFile, \"pub-key-file\", \"\", \"Path to output public key file\")\n\tcreateRootKeyCmd.Flags().DurationVar(&rootExpiration, \"expiration\", 0, \"Expiration time for the root key (e.g., 720h,)\")\n\n\tif err := createRootKeyCmd.MarkFlagRequired(\"priv-key-file\"); err != nil {\n\t\tpanic(err)","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/rootkey.go#L9-L45","documentation":"Validation inside create-root-key's RunE (rootkey.go:26): the --expiration flag, parsed by cobra via time.ParseDuration, was <= 0. The flag defaults to 0 and is marked required, so simply omitting it trips cobra's required-flag error first; this specific message means an explicit non-positive value was passed. Note Go durations have no 'd' unit — the '365d' example in the message itself would fail earlier at flag parsing with 'time: unknown unit \"d\" in \"365d\"'.","triggerScenarios":"Running create-root-key with --expiration 0, --expiration 0s, or a negative value such as --expiration -24h; values like 365d or 30d never reach this check because cobra rejects the unit during flag parsing.","commonSituations":"Copy/pasting the error message's own '365d' example; scripting that computes the flag from a variable that evaluated to zero; assuming day units like Kubernetes durations.","solutions":["Pass a positive duration in Go units — hours is most practical: --expiration 8760h for 1 year, 720h for 30 days","Do not use d suffixes; convert days to hours (days * 24) in scripts","If scripting the value, guard it: [ \"$DAYS\" -gt 0 ] && FLAG=\"--expiration $((DAYS * 24))h\""],"exampleFix":"# before\nsigner create-root-key --priv-key-file root.pem --pub-key-file root-public.pem --expiration 365d\n# (cobra: invalid argument \"365d\" for \"--expiration\" flag: time: unknown unit)\n\n# after\nsigner create-root-key --priv-key-file root.pem --pub-key-file root-public.pem --expiration 8760h","handlingStrategy":"validation","validationCode":"func parseExpirationFlag(raw string) (time.Duration, error) {\n    d, err := time.ParseDuration(raw)\n    if err != nil {\n        return 0, fmt.Errorf(\"%q is not a Go duration (use hours, e.g. 8760h): %w\", raw, err)\n    }\n    if d <= 0 {\n        return 0, fmt.Errorf(\"expiration must be positive, got %s\", d)\n    }\n    return d, nil\n}","typeGuard":"func isValidExpiration(s string) bool {\n    d, err := time.ParseDuration(s)\n    return err == nil && d > 0\n}","tryCatchPattern":null,"preventionTips":["Use hour units exclusively (8760h = 1 year); Go has no 'd' suffix","Derive the flag in scripts from a validated day count: --expiration $((days*24))h","Remember cobra already rejects unparsable values, so this error only surfaces explicit zero/negative inputs"],"tags":["go","cli","validation","cobra","duration"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}