fix(backup+admission): drop redundant api.resolvePath on already-absolute paths (Issue #682, supersedes PR #695)#743
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 136cff7247
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // the caller is responsible for resolving it via api.resolvePath. | ||
| // Absolute explicit paths pass through unchanged and must NOT be re-resolved. | ||
| const rawPath = resolveRejectedAuditFilePath(resolvedDbPath, config.admissionControl); | ||
| const filePath = rawPath.startsWith("/") ? rawPath : api.resolvePath(rawPath); |
There was a problem hiding this comment.
Detect absolute audit paths with path.isAbsolute
The new absolute-path guard only checks rawPath.startsWith("/"), which misses Windows absolute forms such as C:\\logs\\rejections.jsonl and \\\\server\\share\\rejections.jsonl. In those cases this branch still calls api.resolvePath(rawPath) on an already-absolute path; under the same strict OpenClaw behavior this patch is addressing, that can return undefined, and the writer will fail at runtime when it reaches dirname(filePath)/appendFile(...), silently dropping rejection-audit writes on Windows setups. Use a platform-aware absolute check (for example path.isAbsolute) before deciding to call api.resolvePath.
Useful? React with 👍 / 👎.
…lute paths (Issue CortexReach#682) Root cause: OpenClaw 2026.4.x strict plugin API — calling api.resolvePath() on an already-absolute path that points outside the workspace root returns undefined. Two call sites were affected: 1. runBackup() — plugin's own 60s-after-start and every-24h internal timer backup crashed with: TypeError [ERR_INVALID_ARG_TYPE]: The path argument must be of type string or an instance of Buffer or URL. Received undefined. 2. createAdmissionRejectionAuditWriter() — same pattern, same root cause. Fix for both: - runBackup(): resolvedDbPath is already absolute; join() directly without re-wrapping in api.resolvePath(). - admission writer: apply same guard — if rawPath starts with '/' (already absolute) use as-is, otherwise call api.resolvePath() for relative paths. Also adds test/admission-rejection-audit-path.test.mjs covering the three resolveRejectedAuditFilePath() path-construction scenarios: (a) default — always returns already-absolute path (no re-resolve needed) (b) explicit relative path — returned as-is for caller to resolve (c) explicit absolute path — returned as-is, must NOT be re-resolved Supersedes PR CortexReach#695 (closed due to inactivity after maintainer review). Maintainer feedback from PR CortexReach#695: - direction confirmed correct - admission audit path same bug flagged as follow-up suggestion - regression test requested
136cff7 to
48b6940
Compare
Summary
Fixes Issue #682 — two code paths crashed on OpenClaw 2026.4.x with:
This PR supersedes PR #695 (closed 2026-05-04 by maintainer due to inactivity after review).
Root Cause
OpenClaw 2026.4.x tightened the plugin API: calling
api.resolvePath()on an already-absolute path that points outside the agent workspace root now returnsundefined.Two call sites had this pattern:
1.
runBackup()— plugin internal timer backupresolvedDbPathis produced byapi.resolvePath(...)at plugin init and is always absolute. InsiderunBackup():Fix:
resolvedDbPathis already absolute — join directly without re-wrapping.Defensive guard: Additionally guards against
resolvedDbPathbeingundefined(edge case whereapi.resolvePath()returnsundefinedfor empty-string input rather than throwing), preventing the TypeError at thejoin()call site.2.
createAdmissionRejectionAuditWriter()— admission audit sidecarSame redundant wrapper pattern:
resolveRejectedAuditFilePath()returns:join(dbPath, "..", "admission-audit", "rejections.jsonl")— already absolute"data/audit/rejections.jsonl"— caller must resolve"/var/log/memory/rejections.jsonl"— must NOT be re-resolvedFix: Guard with
startsWith("/")— only resolve relative paths.Changes
index.ts—runBackup()(~line 4015)async function runBackup() { try { + // ── Defensive: guard against undefined resolvedDbPath ───────────────── + // api.resolvePath() may return undefined when config.dbPath is an + // empty string or an unhandled edge-case, rather than throwing. + if (!resolvedDbPath || typeof resolvedDbPath !== "string") { + api.logger.warn( + `memory-lancedb-pro: backup skipped — resolvedDbPath is "${String(resolvedDbPath)}"`, + ); + return; + } + // resolvedDbPath is already absolute; wrapping it again triggered // api.resolvePath(absolute-path) → undefined in OpenClaw 2026.4.x. const backupDir = join(resolvedDbPath, "..", "backups"); + + // ── Secondary guard: ensure join() also returned a valid string ────── + if (!backupDir || typeof backupDir !== "string") { + api.logger.warn( + `memory-lancedb-pro: backup skipped — backupDir resolved to "${String(backupDir)}"`, + ); + return; + } await mkdir(backupDir, { recursive: true });index.ts—createAdmissionRejectionAuditWriter()(~line 1675)test/admission-rejection-audit-path.test.mjs— new regression test (11 cases)Covers all three
resolveRejectedAuditFilePath()scenarios:Relationship to PR #695
PR #695 (closed 2026-05-04, commit
82b0b6b) was opened by an external contributor and closed due to inactivity after maintainer review. Maintainer feedback:This PR:
runBackup()fix from PR fix(backup): drop redundant api.resolvePath on already-absolute path (Issue #682) #695 ✅runBackup()againstundefinedpath values ✅Verification
Test Plan
backup completedlog appears within ~60s (no TypeError)npm test— no regression