{"record":{"id":"73f369fd03120324","repo":"kgretzky/evilginx2","slug":"time-duration-value-needs-to-start-with-a-number","errorCode":null,"errorMessage":"time duration value needs to start with a number","messagePattern":"time duration value needs to start with a number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/utils.go","lineNumber":124,"sourceCode":"\t\t\t\t\t\tcase 'd':\n\t\t\t\t\t\t\tdays = val\n\t\t\t\t\t\tcase 'h':\n\t\t\t\t\t\t\thours = val\n\t\t\t\t\t\tcase 'm':\n\t\t\t\t\t\t\tminutes = val\n\t\t\t\t\t\tcase 's':\n\t\t\t\t\t\t\tseconds = val\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\terr = fmt.Errorf(\"you can only use time duration types in following order: 'd' > 'h' > 'm' > 's'\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\terr = fmt.Errorf(\"unknown time duration type: '%s', you can use only 'd', 'h', 'm' or 's'\", string(c))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(\"time duration value needs to start with a number\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\ts_num = \"\"\n\t\t}\n\t}\n\tt_dur = time.Duration(days)*24*time.Hour + time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second\n\treturn\n}\n\nfunc GetDurationString(t_now time.Time, t_expire time.Time) (ret string) {\n\tvar days, hours, minutes, seconds int64\n\tret = \"\"\n\n\tif t_expire.After(t_now) {\n\t\tt_dur := t_expire.Sub(t_now)\n\t\tif t_dur > 0 {\n\t\t\tdays = int64(t_dur / (24 * time.Hour))\n\t\t\tt_dur -= time.Duration(days) * (24 * time.Hour)","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/kgretzky/evilginx2/blob/4c0988a1d9db4d172a185e979a38bfd0efdb5830/core/utils.go#L106-L142","documentation":"ParseDurationString requires each unit token to be preceded by a numeric value. If it hits a unit character (or any non-digit) while s_num is empty — meaning no digits accumulated since the last unit — it reports that the duration value must start with a number.","triggerScenarios":"Strings that begin with a unit or contain consecutive units without a number between them, e.g. 'h30m', '1h m', '5m s', or a leading '-'.","commonSituations":"Typos like '1hh', duplicated separators, or pasting '±30m' / '-5m' strings where the sign or symbol precedes the digits.","solutions":["Ensure every unit is directly preceded by digits: '1h30m', not '1h m'.","Remove duplicate or dangling unit characters.","Do not use signs or prefixes; the parser only accepts plain non-negative integers followed by a unit."],"exampleFix":"// before\nParseDurationString(\"1h m\")\n// after\nParseDurationString(\"1h5m\")","handlingStrategy":"validation","validationCode":"var durRe = regexp.MustCompile(`^(?:\\d+[dhms])+$`)\nif !durRe.MatchString(durStr) {\n    return fmt.Errorf(\"duration %q must be number+unit pairs, e.g. 1h30m\", durStr)\n}\nd, err := core.ParseDurationString(durStr)","typeGuard":"func isNumberUnitPairs(s string) bool {\n    prevDigit := false\n    for i, c := range s {\n        isDigit := c >= '0' && c <= '9'\n        if i == 0 && !isDigit { return false }\n        if !isDigit && !prevDigit { return false }\n        if !isDigit && !strings.ContainsRune(\"dhms\", c) { return false }\n        prevDigit = isDigit\n    }\n    return prevDigit\n}","tryCatchPattern":"d, err := core.ParseDurationString(input)\nif err != nil {\n    if strings.Contains(err.Error(), \"start with a number\") {\n        log.Printf(\"%q has a unit without a preceding number\", input)\n        return\n    }\n    return err\n}","preventionTips":["Ensure each unit is immediately preceded by digits","Remove dangling or duplicated units like '1hh' or '1h m'","Trim signs and symbols; only non-negative integers are accepted"],"tags":["parsing","duration","validation"],"backgroundTag":"invalid-duration-format","analyzedSha":"4c0988a1d9db4d172a185e979a38bfd0efdb5830","analyzedAt":"2026-09-05T19:23:07.238Z","contentChangedAt":"2026-09-05T19:23:07.238Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}