{"record":{"id":"51b76e5847ca969c","repo":"AlistGo/alist","slug":"invalid-expire-at","errorCode":null,"errorMessage":"invalid expire_at","messagePattern":"invalid expire_at","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/handles/share.go","lineNumber":278,"sourceCode":"func parseShareExpireAt(raw string) (*time.Time, error) {\n\tvalue := strings.TrimSpace(raw)\n\tif value == \"\" {\n\t\treturn nil, nil\n\t}\n\tif parsed, err := time.Parse(time.RFC3339, value); err == nil {\n\t\treturn &parsed, nil\n\t}\n\tlayouts := []string{\n\t\t\"2006-01-02T15:04:05\",\n\t\t\"2006-01-02T15:04\",\n\t\t\"2006-01-02 15:04:05\",\n\t}\n\tfor _, layout := range layouts {\n\t\tif parsed, err := time.ParseInLocation(layout, value, time.Local); err == nil {\n\t\t\treturn &parsed, nil\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"invalid expire_at\")\n}\n\nfunc resolveShareExpireAt(expireAt string, expireHours int64) (*time.Time, error) {\n\tif strings.TrimSpace(expireAt) != \"\" {\n\t\treturn parseShareExpireAt(expireAt)\n\t}\n\tif expireHours < 0 {\n\t\treturn nil, fmt.Errorf(\"expire_hours must be 0 or greater\")\n\t}\n\tif expireHours == 0 {\n\t\treturn nil, nil\n\t}\n\texpires := time.Now().Add(time.Duration(expireHours) * time.Hour)\n\treturn &expires, nil\n}\n\nfunc sharePasswordMatched(share *model.Share, password string) bool {\n\tif !share.HasPassword() {","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/handles/share.go#L260-L296","documentation":"Returned by parseShareExpireAt (server/handles/share.go:278) when the expire_at string of a share request matches none of the accepted layouts: RFC3339 (e.g. 2026-08-15T12:00:00Z), 2006-01-02T15:04:05, 2006-01-02T15:04, or 2006-01-02 15:04:05 — the last three parsed in the server's local timezone. The field is optional; an empty string means 'no expiry', so only a non-empty unparseable string triggers this error.","triggerScenarios":"Share create/update with expire_at=\"2026/08/15\" (slashes), \"2026-08-15\" (date only, no time), \"tomorrow\", or an impossible date like \"2026-02-30\".","commonSituations":"Date pickers emitting locale-specific formats; clients copying a date-only value; timezone suffixes that are not valid RFC3339 offsets.","solutions":["Use full RFC3339 with an explicit offset, e.g. 2026-08-15T12:00:00Z","Or include a time component in one of the three local layouts (2026-08-15T12:00:00)","If you only need a relative expiry, drop expire_at and send expire_hours instead"],"exampleFix":"// before\n{\"expire_at\": \"2026/08/15 14:00\"}\n// after\n{\"expire_at\": \"2026-08-15T14:00:00Z\"}","handlingStrategy":"validation","validationCode":"layouts := []string{time.RFC3339, \"2006-01-02T15:04:05\", \"2006-01-02T15:04\", \"2006-01-02 15:04:05\"}\nfunc validExpireAt(s string) bool {\n  s = strings.TrimSpace(s)\n  if s == \"\" { return true }\n  for _, l := range layouts { if _, err := time.Parse(l, s); err == nil { return true } }\n  return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Standardize on RFC3339 with an explicit UTC offset in clients","Prefer expire_hours for relative deadlines","Remember date-only strings are rejected — a time component is required"],"tags":["share","time-parsing","validation"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}