From afbce7357086de644a48b08e0289576bb19be930 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Thu, 12 Feb 2026 16:08:41 -0500 Subject: [PATCH] fix: use os.tmpdir fallback paths for temp files (#14985) Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: 347c689407037a05be0717209660076c6a07d0ec Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com> Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com> Reviewed-by: @gumadeiras --- CHANGELOG.md | 1 + src/browser/pw-tools-core.downloads.ts | 3 ++- src/browser/routes/agent.debug.ts | 3 ++- .../register.files-downloads.ts | 2 +- src/logging/logger.ts | 16 +++++++++++++--- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4339742985..ba5277fd55a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai - Cron: isolate scheduler errors so one bad job does not break all jobs. (#14385) Thanks @MarvinDontPanic. - Cron: prevent one-shot `at` jobs from re-firing on restart after skipped/errored runs. (#13878) Thanks @lailoo. - Heartbeat: prevent scheduler stalls on unexpected run errors and avoid immediate rerun loops after `requests-in-flight` skips. (#14901) Thanks @joeykrug. +- Logging/Browser: fall back to `os.tmpdir()/openclaw` for default log, browser trace, and browser download temp paths when `/tmp/openclaw` is unavailable. - WhatsApp: convert Markdown bold/strikethrough to WhatsApp formatting. (#14285) Thanks @Raikan10. - WhatsApp: allow media-only sends and normalize leading blank payloads. (#14408) Thanks @karimnaguib. - WhatsApp: default MIME type for voice messages when Baileys omits it. (#14444) Thanks @mcaxtr. diff --git a/src/browser/pw-tools-core.downloads.ts b/src/browser/pw-tools-core.downloads.ts index 60788d8fbdd..1f029a48377 100644 --- a/src/browser/pw-tools-core.downloads.ts +++ b/src/browser/pw-tools-core.downloads.ts @@ -1,6 +1,7 @@ import type { Page } from "playwright-core"; import crypto from "node:crypto"; import fs from "node:fs/promises"; +import os from "node:os"; import path from "node:path"; import { ensurePageState, @@ -20,7 +21,7 @@ import { function buildTempDownloadPath(fileName: string): string { const id = crypto.randomUUID(); const safeName = fileName.trim() ? fileName.trim() : "download.bin"; - return path.join("/tmp/openclaw/downloads", `${id}-${safeName}`); + return path.join(os.tmpdir(), "openclaw", "downloads", `${id}-${safeName}`); } function createPageDownloadWaiter(page: Page, timeoutMs: number) { diff --git a/src/browser/routes/agent.debug.ts b/src/browser/routes/agent.debug.ts index 62056de8c0d..ec4c944c978 100644 --- a/src/browser/routes/agent.debug.ts +++ b/src/browser/routes/agent.debug.ts @@ -1,5 +1,6 @@ import crypto from "node:crypto"; import fs from "node:fs/promises"; +import os from "node:os"; import path from "node:path"; import type { BrowserRouteContext } from "../server-context.js"; import type { BrowserRouteRegistrar } from "./types.js"; @@ -131,7 +132,7 @@ export function registerBrowserAgentDebugRoutes( return; } const id = crypto.randomUUID(); - const dir = "/tmp/openclaw"; + const dir = path.join(os.tmpdir(), "openclaw"); await fs.mkdir(dir, { recursive: true }); const tracePath = out.trim() || path.join(dir, `browser-trace-${id}.zip`); await pw.traceStopViaPlaywright({ diff --git a/src/cli/browser-cli-actions-input/register.files-downloads.ts b/src/cli/browser-cli-actions-input/register.files-downloads.ts index efbc40363d1..316faae3e73 100644 --- a/src/cli/browser-cli-actions-input/register.files-downloads.ts +++ b/src/cli/browser-cli-actions-input/register.files-downloads.ts @@ -57,7 +57,7 @@ export function registerBrowserFilesAndDownloadsCommands( browser .command("waitfordownload") .description("Wait for the next download (and save it)") - .argument("[path]", "Save path (default: /tmp/openclaw/downloads/...)") + .argument("[path]", "Save path (default: os.tmpdir()/openclaw/downloads/...)") .option("--target-id ", "CDP target id (or unique prefix)") .option( "--timeout-ms ", diff --git a/src/logging/logger.ts b/src/logging/logger.ts index 819a14a8aba..eef171fa033 100644 --- a/src/logging/logger.ts +++ b/src/logging/logger.ts @@ -1,5 +1,6 @@ import fs from "node:fs"; import { createRequire } from "node:module"; +import os from "node:os"; import path from "node:path"; import { Logger as TsLogger } from "tslog"; import type { OpenClawConfig } from "../config/types.js"; @@ -8,9 +9,18 @@ import { readLoggingConfig } from "./config.js"; import { type LogLevel, levelToMinLevel, normalizeLogLevel } from "./levels.js"; import { loggingState } from "./state.js"; -// Pin to /tmp so mac Debug UI and docs match; os.tmpdir() can be a per-user -// randomized path on macOS which made the “Open log” button a no-op. -export const DEFAULT_LOG_DIR = "/tmp/openclaw"; +// Prefer /tmp/openclaw so macOS Debug UI and docs match, but fall back to +// os.tmpdir() on platforms where /tmp is read-only (e.g. Termux/Android). +function resolveDefaultLogDir(): string { + try { + fs.mkdirSync("/tmp/openclaw", { recursive: true }); + return "/tmp/openclaw"; + } catch { + return path.join(os.tmpdir(), "openclaw"); + } +} + +export const DEFAULT_LOG_DIR = resolveDefaultLogDir(); export const DEFAULT_LOG_FILE = path.join(DEFAULT_LOG_DIR, "openclaw.log"); // legacy single-file path const LOG_PREFIX = "openclaw";