Skip to content

Commit 7cc30a6

Browse files
TpRippyclaude
authored andcommitted
fix: handle Windows-style session paths when running on POSIX
When sessions.json is written by a Windows host but read inside a Linux Docker container, paths like "C:\Users\...\abc.jsonl" are treated as relative by path.isAbsolute (which only recognises POSIX absolutes). This causes resolvePathWithinSessionsDir to concatenate the Windows path onto the container base, producing broken paths like "/home/node/.openclaw/.../C:\Users\...\abc.jsonl". Add isWindowsAbsoluteOnPosix() guard that detects drive-letter paths on POSIX and extracts the leaf filename, resolving it within the sessions directory instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d8d441c commit 7cc30a6

3 files changed

Lines changed: 37 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ Docs: https://docs.openclaw.ai
181181

182182
### Fixes
183183

184+
- Sessions/Windows: resolve persisted Windows-style absolute transcript paths by filename on POSIX so imported session stores no longer create literal drive-letter paths under the local sessions directory. (#50116) Thanks @RIPRODUCTIONS.
184185
- fix(discord): gate user allowlist name resolution [AI]. (#79002) Thanks @pgondhi987.
185186
- fix(msteams): gate startup user allowlist resolution [AI]. (#79003) Thanks @pgondhi987.
186187
- Harden macOS shell wrapper allowlist parsing [AI]. (#78518) Thanks @pgondhi987.

src/config/sessions.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,22 @@ describe("sessions", () => {
662662
);
663663
});
664664

665+
it.runIf(process.platform !== "win32")(
666+
"maps Windows absolute sessionFile paths to the local sessions dir",
667+
() => {
668+
const sessionsDir = "/tmp/openclaw/agents/main/sessions";
669+
const sessionFile = resolveSessionFilePath(
670+
"sess-1",
671+
{
672+
sessionFile: String.raw`C:\Users\alice\.openclaw\agents\main\sessions\sess-2.jsonl`,
673+
},
674+
{ sessionsDir },
675+
);
676+
677+
expect(sessionFile).toBe(path.resolve(sessionsDir, "sess-2.jsonl"));
678+
},
679+
);
680+
665681
it("resolves cross-agent paths when OPENCLAW_STATE_DIR differs from stored paths", () => {
666682
withStateDir(path.resolve("/different/state"), () => {
667683
const originalBase = path.resolve("/original/state");

src/config/sessions/paths.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
77
import { resolveStateDir } from "../paths.js";
88
import { isCompactionCheckpointTranscriptFileName } from "./artifacts.js";
99

10+
function isWindowsAbsoluteOnPosix(candidate: string): boolean {
11+
if (path.sep === "\\") {
12+
return false;
13+
}
14+
return /^[A-Za-z]:[/\\]/.test(candidate);
15+
}
16+
17+
function extractBasename(candidate: string): string {
18+
const parts = candidate.split(/[/\\]/);
19+
return parts[parts.length - 1] ?? candidate;
20+
}
21+
1022
function resolveAgentSessionsDir(
1123
agentId?: string,
1224
env: NodeJS.ProcessEnv = process.env,
@@ -182,6 +194,14 @@ function resolvePathWithinSessionsDir(
182194
if (!trimmed) {
183195
throw new Error("Session file path must not be empty");
184196
}
197+
198+
if (isWindowsAbsoluteOnPosix(trimmed)) {
199+
const basename = extractBasename(trimmed);
200+
if (basename && basename !== "." && basename !== "..") {
201+
return path.resolve(path.resolve(sessionsDir), basename);
202+
}
203+
}
204+
185205
const resolvedBase = path.resolve(sessionsDir);
186206
const realBase = safeRealpathSync(resolvedBase) ?? resolvedBase;
187207
// Normalize absolute paths that are within the sessions directory.

0 commit comments

Comments
 (0)