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

  1. Set LibraryFileName to the bare DLL file name only (e.g. LibraryFileName=MyMod.dll) — no paths, drive letters, colons, or '.'/'..'.
  2. Reinstall the mod from its official source if you didn't author the metadata yourself.
  3. 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

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


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)