{"record":{"id":"255032b690cc4b82","repo":"dutchcoders/transfer.sh","slug":"file-not-found","errorCode":null,"errorMessage":"File not found","messagePattern":"File not found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"server/handlers.go","lineNumber":995,"sourceCode":"\tzw := zip.NewWriter(w)\n\n\tfor _, key := range strings.Split(files, \",\") {\n\t\tkey = resolveKey(key, s.proxyPath)\n\n\t\ttoken := strings.Split(key, \"/\")[0]\n\t\tfilename := sanitize(strings.Split(key, \"/\")[1])\n\n\t\tif _, err := s.checkMetadata(r.Context(), token, filename, true); err != nil {\n\t\t\ts.logger.Printf(\"Error metadata: %s\", err.Error())\n\t\t\tcontinue\n\t\t}\n\n\t\treader, _, err := s.storage.Get(r.Context(), token, filename, nil)\n\t\tdefer storage.CloseCheck(reader)\n\n\t\tif err != nil {\n\t\t\tif s.storage.IsNotExist(err) {\n\t\t\t\thttp.Error(w, \"File not found\", 404)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ts.logger.Printf(\"%s\", err.Error())\n\t\t\thttp.Error(w, \"Could not retrieve file.\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\theader := &zip.FileHeader{\n\t\t\tName:   strings.Split(key, \"/\")[1],\n\t\t\tMethod: zip.Store,\n\n\t\t\tModified: time.Now().UTC(),\n\t\t}\n\n\t\tfw, err := zw.CreateHeader(header)\n\n\t\tif err != nil {","sourceCodeStart":977,"sourceCodeEnd":1013,"githubUrl":"https://github.com/dutchcoders/transfer.sh/blob/c37bfd95797fd6da8a6da53fc13d191994b3f687/server/handlers.go#L977-L1013","documentation":"zipHandler iterates the keys under an archive token and calls s.storage.Get for each file. When Get returns a not-exist error, the handler responds with HTTP 404 'File not found'. One of the files listed under the token is missing from the storage backend.","triggerScenarios":"s.storage.Get returns an error for which s.storage.IsNotExist(err) is true while building the zip: a file listed in the token's key set was deleted, expired via TTL, or never fully persisted.","commonSituations":"A file expired (TTL elapsed) between listing and fetching; local storage directory cleaned by a cron job; one file in a multi-file upload failed to persist; race between a concurrent delete and the zip download.","solutions":["Re-upload the missing file(s) and request the zip again.","Check TTL/expiry settings and the storage backend to confirm which file was purged.","List and GET the token's files individually to identify the missing one.","Avoid racing delete operations against active archive downloads."],"exampleFix":"// before\nreader, _, err := s.storage.Get(r.Context(), token, filename, nil)\nif err != nil {\n    if s.storage.IsNotExist(err) {\n        http.Error(w, \"File not found\", 404)\n// after\nreader, _, err := s.storage.Get(r.Context(), token, filename, nil)\nif err != nil {\n    if s.storage.IsNotExist(err) {\n        s.logger.Printf(\"zip: missing %s/%s\", token, filename)\n        http.Error(w, \"File not found: \"+filename, 404)","handlingStrategy":"validation","validationCode":"// Pre-flight each file in the archive token before requesting the zip:\nfor _, f := range files {\n    resp, err := http.Head(baseURL + \"/\" + token + \"/\" + f)\n    if err != nil || resp.StatusCode == 404 {\n        // f is missing — re-upload before zipping\n    }\n}","typeGuard":null,"tryCatchPattern":"// Treat 404 as 'file missing from token': identify which file, re-upload, retry\nif resp.StatusCode == 404 { reuploadMissingFile(); retryArchiveDownload() }","preventionTips":["Set TTLs longer than the expected lifetime of archive tokens.","Disable or align cleanup cron jobs with active download windows.","Avoid deleting files while an archive download of the token is in flight.","Download files individually first to detect missing objects before zipping."],"tags":["http-404","storage","not-found"],"backgroundTag":"file-not-found","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"}