{"record":{"id":"a95003c9354dca1e","repo":"dutchcoders/transfer.sh","slug":"maxdate-expired","errorCode":null,"errorMessage":"maxDate expired","messagePattern":"maxDate expired","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"server/handlers.go","lineNumber":888,"sourceCode":"func (s *Server) checkMetadata(ctx context.Context, token, filename string, increaseDownload bool) (metadata, error) {\n\ts.lock(token, filename)\n\tdefer s.unlock(token, filename)\n\n\tvar metadata metadata\n\n\tr, _, err := s.storage.Get(ctx, token, fmt.Sprintf(\"%s.metadata\", filename), nil)\n\tdefer storage.CloseCheck(r)\n\n\tif err != nil {\n\t\treturn metadata, err\n\t}\n\n\tif err := json.NewDecoder(r).Decode(&metadata); err != nil {\n\t\treturn metadata, err\n\t} else if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads {\n\t\treturn metadata, errors.New(\"maxDownloads expired\")\n\t} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) {\n\t\treturn metadata, errors.New(\"maxDate expired\")\n\t} else if metadata.MaxDownloads != -1 && increaseDownload {\n\t\t// todo(nl5887): mutex?\n\n\t\t// update number of downloads\n\t\tmetadata.Downloads++\n\n\t\tbuffer := &bytes.Buffer{}\n\t\tif err := json.NewEncoder(buffer).Encode(metadata); err != nil {\n\t\t\treturn metadata, errors.New(\"could not encode metadata\")\n\t\t} else if err := s.storage.Put(ctx, token, fmt.Sprintf(\"%s.metadata\", filename), buffer, \"text/json\", uint64(buffer.Len())); err != nil {\n\t\t\treturn metadata, errors.New(\"could not save metadata\")\n\t\t}\n\t}\n\n\treturn metadata, nil\n}\n\nfunc (s *Server) checkDeletionToken(ctx context.Context, deletionToken, token, filename string) error {","sourceCodeStart":870,"sourceCodeEnd":906,"githubUrl":"https://github.com/dutchcoders/transfer.sh/blob/c37bfd95797fd6da8a6da53fc13d191994b3f687/server/handlers.go#L870-L906","documentation":"checkMetadata also enforces an optional expiry date stored in the file's metadata. If the metadata has a non-zero MaxDate and the current time is after it, the server refuses to serve the file and returns this error. It means the upload's time-to-live, as chosen by the sender, has elapsed.","triggerScenarios":"Any download request (GET/HEAD/preview/zip/tar/tar.gz) for a token whose metadata contains MaxDate in the past, e.g. an upload created with a Max-Date header (or purge days) that has since passed.","commonSituations":"Recipient opens a shared link after the expiry window set by the sender; clocks skewed between server and client making a link appear expired early; very short expirations (minutes) combined with email/social delays.","solutions":["Request the sender re-upload and share a fresh link with a later Max-Date.","Upload with a longer expiry (larger --purge-days / appropriate Max-Date header) when the link must live longer.","Verify server clock/NTP sync if files expire sooner than expected.","If you control the deployment, purge/expiry policy can be tuned in checkMetadata or the purge job."],"exampleFix":"// before\ncurl -H 'Max-Date: 2026-01-01T00:00:00Z' -T f https://host/f  # already past\n// after\ncurl -H 'Max-Date: 2026-12-31T00:00:00Z' -T f https://host/f","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := http.Get(url)\nif err == nil {\n\tbody, _ := io.ReadAll(resp.Body)\n\tif strings.Contains(string(body), \"maxDate expired\") {\n\t\t// link expired: ask sender to re-upload\n\t}\n}","preventionTips":["Pick Max-Date well beyond the expected sharing window.","Keep server clocks NTP-synced so expiry behaves predictably.","Track link creation dates and re-share before expiry when needed."],"tags":["metadata","expiration","ttl","file-transfer"],"backgroundTag":"link-expired","analyzedSha":"c37bfd95797fd6da8a6da53fc13d191994b3f687","analyzedAt":"2026-09-05T10:21:07.548Z","contentChangedAt":"2026-09-05T10:21:07.548Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}