ramensoftware/windhawk · error · std::runtime_error
Invalid LibraryFileName value
Error message
Invalid LibraryFileName value
What it means
GetModLibraryPath rejects a LibraryFileName that is not a bare file name: paths containing separators, drive/root components, a colon, '.' or '..' are refused so the joined path cannot escape the mods storage folder. Any such value throws runtime_error('Invalid LibraryFileName value'). This is a deliberate path-traversal guard.
Solutions
- Set LibraryFileName to the bare DLL file name only (e.g. LibraryFileName=MyMod.dll) — no paths, drive letters, colons, or '.'/'..'.
- Reinstall the mod from its official source if you didn't author the metadata yourself.
- If authoring, keep the DLL inside the per-mod folder and reference it by name only.
Example fix
// before LibraryFileName=C:\Users\me\build\MyMod.dll // after LibraryFileName=MyMod.dll
Defensive patterns
Strategy: validation
Validate before calling
// caller-side pre-check mirroring the guard
bool IsBareFileName(std::wstring_view name) {
if (name.empty() || name == L"." || name == L"..") return false;
if (name.find(L':') != std::wstring_view::npos) return false;
if (name.find(L'\\') != std::wstring_view::npos ||
name.find(L'/') != std::wstring_view::npos) return false;
return true;
} Type guard
bool IsSafeLibraryFileName(std::wstring_view v) {
std::filesystem::path p(v);
return !v.empty() && p == p.filename() &&
v.find(L':') == std::wstring_view::npos &&
v != L"." && v != L"..";
} Try / catch
try {
auto path = GetModLibraryPath(libFile);
} catch (const std::runtime_error& e) {
if (std::string_view(e.what()) == "Invalid LibraryFileName value") {
LOG(L"Rejecting mod with unsafe LibraryFileName");
}
} Prevention
- Always store only the bare DLL name in LibraryFileName.
- Treat any path-like value in mod metadata as a red flag (possible malicious mod).
- Normalize/strip user-supplied paths to filename() before writing metadata.
- Keep the traversal guard; never 'fix' errors like this by widening accepted values.
When it happens
Trigger: A mod's LibraryFileName set to something like '..\\evil.dll', 'C:\\Windows\\x.dll', 'sub\\mod.dll', 'foo:bar' or '.' — anything whose std::filesystem value differs from its filename() component.
Common situations: Malicious or carelessly authored mod packages trying to point outside the mods directory; users copying a full path into LibraryFileName instead of just the DLL name; Windows stream syntax 'name:stream' in the value.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Missing LibraryFileName value
- Initial settings arrays must contain at least one template…
- Invalid object array schema definition.
- Unknown setting type for value
- Mod id must be specified in the source code
AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12).
Data as JSON: /api/errors/58a2f4a8ef92e001.
Report an issue: GitHub.
Appendix: source
Thrown at src/windhawk/engine/mod.cpp:263
listTakesEveryProcess(patterns.include)) ||
listTakesEveryProcess(patterns.includeCustom);
}
// The library named by the mod's settings. It must be a file in the mods
// folder, so only a bare file name is accepted: a separator, a root name or a
// ".." would make the joined path escape the folder. A ':' is rejected on its
// own, since a root name is a single drive letter and "foo:bar", a stream on
// the folder, is its own filename().
std::filesystem::path GetModLibraryPath(std::wstring_view libraryFileName) {
if (libraryFileName.empty()) {
throw std::runtime_error("Missing LibraryFileName value");
}
std::filesystem::path fileName(libraryFileName);
if (fileName != fileName.filename() ||
libraryFileName.find(L':') != libraryFileName.npos ||
libraryFileName == L"." || libraryFileName == L"..") {
throw std::runtime_error("Invalid LibraryFileName value");
}
return StorageManager::GetInstance().GetModsPath() / fileName;
}
Mod::ChangeMarker MakeChangeMarker(PortableSettings& settings) {
return {
.libraryFileName = settings.GetString(L"LibraryFileName").value_or(L""),
.settingsChangeTime =
settings.GetInt(L"SettingsChangeTime").value_or(0),
};
}
// Whether the mod's library carries the tool mod marker in its export table.
bool DoesModExportToolModMarker(PCWSTR modName, PortableSettings& settings) {
// A library which can't be read isn't turned into a tool mod; the load path
// is what reports such a failure to the user.
try {View on GitHub (pinned to 61d99ed8e1)