{"record":{"id":"f666ed8b22ea66e5","repo":"benbjohnson/litestream","slug":"invalid-temp-file-name-q","errorCode":null,"errorMessage":"invalid temp file name: %q","messagePattern":"invalid temp file name: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":379,"sourceCode":"\t})\n\treturn vfs.tempDir, vfs.tempDirErr\n}\n\nfunc (vfs *VFS) canonicalTempName(name string) string {\n\tif name == \"\" {\n\t\treturn \"\"\n\t}\n\tname = filepath.Clean(name)\n\tif name == \".\" || name == string(filepath.Separator) {\n\t\treturn \"\"\n\t}\n\treturn name\n}\n\nfunc tempFilenameFromCanonical(canonical string) (string, error) {\n\tbase := filepath.Base(canonical)\n\tif base == \".\" || base == string(filepath.Separator) {\n\t\treturn \"\", fmt.Errorf(\"invalid temp file name: %q\", canonical)\n\t}\n\n\th := fnv.New64a()\n\tif _, err := h.Write([]byte(canonical)); err != nil {\n\t\treturn \"\", fmt.Errorf(\"hash temp name: %w\", err)\n\t}\n\treturn fmt.Sprintf(\"%s-%016x\", base, h.Sum64()), nil\n}\n\nfunc (vfs *VFS) openTempFile(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite3vfs.OpenFlag, error) {\n\tdir, err := vfs.ensureTempDir()\n\tif err != nil {\n\t\treturn nil, flags, err\n\t}\n\tdeleteOnClose := flags&sqlite3vfs.OpenDeleteOnClose != 0 || name == \"\"\n\tvar f *os.File\n\tvar onClose func()\n\tif name == \"\" {","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L361-L397","documentation":"tempFilenameFromCanonical derives a local temp-file name from a canonical SQLite file name by taking filepath.Base and rejecting names whose base is \".\" or a path separator. This error means the canonical name has no usable base component, so a deterministic temp file name cannot be built. It is raised before a FNV hash is computed.","triggerScenarios":"SQLite passes an unusual canonical name to xOpen that triggers openTempFile, and the name's filepath.Base evaluates to \".\" (e.g. name ends with a separator or is \".\") or equals the OS separator.","commonSituations":"Constructing file URIs with trailing slashes (\"file:/tmp/foo/\") so the base collapses to \".\"; passing a directory instead of a file path; bugs in wrapper code that strip the file name and hand the VFS only a directory path.","solutions":["Remove trailing slashes from the database/journal path before opening it through the VFS.","Pass a real file name, not a directory, to SQLite when using the litestream VFS.","Log the offending canonical name (it is quoted in the error) and fix the code that generates it.","If SQLite itself generates such names, capture a reproducible xOpen trace and report upstream."],"exampleFix":"// before\nname := \"file:/var/tmp/dbdir/\" // base == \".\"\n\n// after\nname := \"file:/var/tmp/dbdir/app.db\" // base == \"app.db\"","handlingStrategy":"validation","validationCode":"func validCanonical(name string) bool {\n    b := filepath.Base(name)\n    return b != \".\" && b != string(filepath.Separator) && name != \"\"\n}\n// call before opening: if !validCanonical(name) { fix path }","typeGuard":"func isRealFilePath(p string) bool {\n    return filepath.Base(p) != \".\" && filepath.Base(p) != string(filepath.Separator)\n}","tryCatchPattern":null,"preventionTips":["Strip trailing slashes from file paths/URIs before opening.","Never pass directory paths as database names.","Assert file-name validity in config loading, before SQLite sees it.","Unit-test open paths that go through user-supplied config."],"tags":["go","path-validation","vfs","filename"],"backgroundTag":"invalid-argument-value","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"}