{"record":{"id":"de08728548c2c09f","repo":"restic/restic","slug":"invalid-writeablefiletype","errorCode":null,"errorMessage":"invalid WriteableFileType","messagePattern":"invalid WriteableFileType","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/restic/filetype.go","lineNumber":51,"sourceCode":"\t\ts = \"config\"\n\t}\n\treturn s\n}\n\n// WriteableFileType defines the different data types that can be modified via SaveUnpacked or RemoveUnpacked.\ntype WriteableFileType FileType\n\nconst (\n\t// WriteableSnapshotFile is the WriteableFileType for snapshots.\n\tWriteableSnapshotFile = WriteableFileType(SnapshotFile)\n)\n\nfunc (w *WriteableFileType) ToFileType() FileType {\n\tswitch *w {\n\tcase WriteableSnapshotFile:\n\t\treturn SnapshotFile\n\tdefault:\n\t\tpanic(\"invalid WriteableFileType\")\n\t}\n}\n\ntype FileTypes interface {\n\tFileType | WriteableFileType\n}\n","sourceCodeStart":33,"sourceCodeEnd":58,"githubUrl":"https://github.com/restic/restic/blob/a80be1478a4c537f8396e0db2b05120aa78f11e0/internal/restic/filetype.go#L33-L58","documentation":"WriteableFileType is a restricted subset of FileType that lists only repository file types the archiver is allowed to write; today that is exactly one value, WriteableSnapshotFile (= SnapshotFile). ToFileType() maps such a value back to the general FileType. The panic fires when ToFileType() is called on any other value, i.e. a WriteableFileType that was produced by blindly casting an unwritable FileType (data, index, key, lock, config) instead of using the defined constant. It is a compile-time-style type invariant enforced at runtime, not an I/O or environment error.","triggerScenarios":"Calling ToFileType() on WriteableFileType(SnapshotFile) succeeds; calling it on a cast of any other FileType (e.g. WriteableFileType(DataFile), WriteableFileType(KeyFile)) panics, as does a zero-value/uninitialized WriteableFileType passed through the conversion. Concretely: wft := WriteableFileType(someFTFromConfig); wft.ToFileType().","commonSituations":"Forks or PRs of restic that add a new writable file type to the repo layout but forget to add it to the const block and the ToFileType switch; code that accepts a FileType from user input/config and casts it to WriteableFileType without validation; passing a partially-initialized options struct where the field stays zero.","solutions":["Only ever construct WriteableFileType from the defined constant WriteableSnapshotFile; never cast an arbitrary FileType to it","If you are extending restic and genuinely need a new writable type, add both a Writeable* constant and a case in ToFileType() in internal/restic/filetype.go, then run the package tests","Guard before conversion: reject values != WriteableSnapshotFile with a normal error instead of letting the panic fire"],"exampleFix":"// before\nwft := WriteableFileType(ConfigFile)\nft := wft.ToFileType() // panic: invalid WriteableFileType\n\n// after\nwft := WriteableSnapshotFile\nft := wft.ToFileType() // ft == SnapshotFile","handlingStrategy":"type-guard","validationCode":"if wft != WriteableSnapshotFile {\n    return fmt.Errorf(\"unsupported writable file type: %v\", FileType(wft))\n}\nft := wft.ToFileType()","typeGuard":"func isValidWriteableFileType(w WriteableFileType) bool {\n    return w == WriteableSnapshotFile\n}","tryCatchPattern":null,"preventionTips":["Never derive WriteableFileType by casting an arbitrary FileType; use the package constants only","Treat this panic as a failed code review of your diff: it means an unwritable type entered a write path","If extending restic, add the constant and the switch case in the same commit and run go test ./internal/restic/"],"tags":["go","restic","panic","invariant","file-type","type-cast"],"backgroundTag":null,"analyzedSha":"a80be1478a4c537f8396e0db2b05120aa78f11e0","analyzedAt":"2026-08-15T15:30:29.928Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}