{"record":{"id":"29d2f5f330886c9b","repo":"benbjohnson/litestream","slug":"vfs-file-not-found-id-d","errorCode":null,"errorMessage":"vfs file not found: id=%d","messagePattern":"vfs file not found: id=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":2859,"sourceCode":"\t\t\treturn infos, nil\n\t\t}\n\t\tif !errors.Is(err, ErrTxNotAvailable) {\n\t\t\treturn nil, fmt.Errorf(\"cannot calc restore plan: %w\", err)\n\t\t}\n\n\t\tf.logger.Debug(\"no backup files available yet, waiting\", \"interval\", f.PollInterval)\n\t\tselect {\n\t\tcase <-time.After(f.PollInterval):\n\t\tcase <-f.ctx.Done():\n\t\t\treturn nil, fmt.Errorf(\"no backup files available: %w\", f.ctx.Err())\n\t\t}\n\t}\n}\n\n// RegisterVFSConnection maps a SQLite connection handle to its VFS file ID.\nfunc RegisterVFSConnection(dbPtr uintptr, fileID uint64) error {\n\tif _, ok := lookupVFSFile(fileID); !ok {\n\t\treturn fmt.Errorf(\"vfs file not found: id=%d\", fileID)\n\t}\n\tvfsConnectionMap.Store(dbPtr, fileID)\n\treturn nil\n}\n\n// UnregisterVFSConnection removes a connection mapping.\nfunc UnregisterVFSConnection(dbPtr uintptr) {\n\tvfsConnectionMap.Delete(dbPtr)\n}\n\n// SetVFSConnectionTime rebuilds the VFS index for a connection at a timestamp.\nfunc SetVFSConnectionTime(dbPtr uintptr, timestamp string) error {\n\tfile, err := vfsFileForConnection(dbPtr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tt, err := parseTimeValue(timestamp)","sourceCodeStart":2841,"sourceCodeEnd":2877,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2841-L2877","documentation":"RegisterVFSConnection maps a SQLite connection pointer (dbPtr) to a VFS file ID. Before storing the mapping it checks that the file ID exists via lookupVFSFile; if not, it refuses to register. This guards against registering connections for files that were already closed or never opened through the VFS.","triggerScenarios":"Calling litestream_vfs_register_connection (or the Go API) with a fileID that was never created by the VFS open, or whose VFSFile was already unregistered/closed.","commonSituations":"Application-level cache of stale file IDs after reopening the database; concurrent close of the VFS file while another goroutine registers its connection; passing the wrong ID after multiple database opens.","solutions":["Open the database through the VFS first and use the file ID returned from that open","Check for double-close races: ensure the file is not unregistered before connection registration","Log/verify the fileID matches the one returned by your latest VFS open call"],"exampleFix":"// before: stale id after reopen\nfileID := cachedID\nlitestreamvfs.RegisterVFSConnection(dbPtr, fileID) // vfs file not found\n// after: use id from the current open\nfile, fileID := litestreamvfs.Open(dbPath)\n_ = litestreamvfs.RegisterVFSConnection(dbPtr, fileID)","handlingStrategy":"validation","validationCode":"func ensureFileRegistered(fileID uint64) error {\n    if _, ok := lookupVFSFile(fileID); !ok {\n        return fmt.Errorf(\"cannot register connection: vfs file %d not open\", fileID)\n    }\n    return nil\n}","typeGuard":"func hasVFSFile(fileID uint64) bool { _, ok := lookupVFSFile(fileID); return ok }","tryCatchPattern":"if err := litestreamvfs.RegisterVFSConnection(dbPtr, fileID); err != nil {\n    if strings.Contains(err.Error(), \"vfs file not found\") {\n        return reopenAndRegister(ctx)\n    }\n    return err\n}","preventionTips":["Always register with the fileID returned by the current open","Never cache file IDs across reopen cycles","Fix any prior registration errors immediately instead of ignoring them"],"tags":["vfs","registration","lifecycle"],"backgroundTag":"resource-not-found","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}