Description
scripts/backup-workspace.sh restore reports Restored 6 items successfully, but the memory directory contents are uploaded to a nested path on the sandbox (memory/memory/<file>.md) instead of the expected flat path (memory/<file>.md).
This is silent data loss from a user perspective: backups appear to roundtrip successfully, but daily memory notes end up at an unreachable path that the OpenClaw dashboard / assistant cannot read. The 5 top-level .md files (SOUL/USER/IDENTITY/AGENTS/MEMORY) restore correctly — only the memory/ directory in DIRS=(memory) is affected.
Expected:
- After
restore, /sandbox/.openclaw/workspace/memory/2026-04-20.md exists with original content.
Actual:
- File ends up at
/sandbox/.openclaw/workspace/memory/memory/2026-04-20.md (nested under an extra memory/).
- Restore output still claims
Restored 6 items so users have no signal that something went wrong.
Root cause (best guess):
Asymmetric trailing-slash semantics in openshell sandbox upload. In scripts/backup-workspace.sh:
- The FILES loop (line ~101) uses
src/${f} (no slash) → WORKSPACE_PATH/ (slash, parent only) and works correctly.
- The DIRS loop (line ~111) uses
src/${d}/ (slash) → WORKSPACE_PATH/${d}/ (slash, with redundant ${d} in destination), which openshell sandbox upload interprets as "upload directory INTO destination dir" → produces nested memory/memory/.
Detected by: PR #3517 — the new test/e2e/test-state-backup-restore.sh strict MemoryDirRestore assertion catches this consistently. Before #3517, the legacy test-deployment-services.sh SKIPped this case with "Memory directory restore may not be supported" which masked the bug indefinitely.
Reproduction Steps
Easiest path — run the E2E test from PR #3517:
bash test/e2e/test-state-backup-restore.sh
Test reports FAIL TC-STATE-01: MemoryDirRestore — memory/2026-04-20.md does NOT exist on sandbox after restore. Inspect the post-test sandbox to confirm nested path.
Manual reproduction:
- Onboard a sandbox:
nemoclaw onboard --non-interactive --yes-i-accept-third-party-software
- Write a marker into the memory directory:
nemoclaw <sandbox-name> connect
# inside sandbox:
mkdir -p /sandbox/.openclaw/workspace/memory
echo "REPRO_MARKER" > /sandbox/.openclaw/workspace/memory/2026-05-15.md
exit
- Back up:
bash scripts/backup-workspace.sh backup <sandbox-name>
→ Reports Backup saved to ~/.nemoclaw/backups/<ts>/ (6 items)
- Verify host backup correctness:
cat ~/.nemoclaw/backups/<ts>/memory/2026-05-15.md # → REPRO_MARKER ✓
- Destroy sandbox:
nemoclaw <sandbox-name> destroy --yes
- Re-onboard:
nemoclaw onboard --non-interactive --yes-i-accept-third-party-software
- Restore:
bash scripts/backup-workspace.sh restore <sandbox-name>
→ Reports Restored 6 items to sandbox <sandbox-name> ✓ (claims success)
- Inspect sandbox file system:
nemoclaw <sandbox-name> connect
# inside sandbox:
find /sandbox/.openclaw -name '2026-05-15.md'
→ Returns /sandbox/.openclaw/workspace/memory/memory/2026-05-15.md (nested) ❌
→ Expected: /sandbox/.openclaw/workspace/memory/2026-05-15.md
Environment
OS: Ubuntu 24.04.4 LTS (Linux x86_64)
Node.js: v22.22.x (via nvm)
Docker: Docker Engine 27.x
NemoClaw: v0.0.41
OpenShell: 0.0.39
Debug Output
Logs
Checklist
Description
scripts/backup-workspace.shrestore reportsRestored 6 itemssuccessfully, but the memory directory contents are uploaded to a nested path on the sandbox (memory/memory/<file>.md) instead of the expected flat path (memory/<file>.md).This is silent data loss from a user perspective: backups appear to roundtrip successfully, but daily memory notes end up at an unreachable path that the OpenClaw dashboard / assistant cannot read. The 5 top-level
.mdfiles (SOUL/USER/IDENTITY/AGENTS/MEMORY) restore correctly — only thememory/directory inDIRS=(memory)is affected.Expected:
restore,/sandbox/.openclaw/workspace/memory/2026-04-20.mdexists with original content.Actual:
/sandbox/.openclaw/workspace/memory/memory/2026-04-20.md(nested under an extramemory/).Restored 6 itemsso users have no signal that something went wrong.Root cause (best guess):
Asymmetric trailing-slash semantics in
openshell sandbox upload. Inscripts/backup-workspace.sh:src/${f} (no slash)→WORKSPACE_PATH/(slash, parent only) and works correctly.src/${d}/(slash) →WORKSPACE_PATH/${d}/(slash, with redundant${d}in destination), whichopenshell sandbox uploadinterprets as "upload directory INTO destination dir" → produces nestedmemory/memory/.Reproduction Steps
Easiest path — run the E2E test from PR #3517:
Test reports
FAIL TC-STATE-01: MemoryDirRestore — memory/2026-04-20.md does NOT exist on sandbox after restore. Inspect the post-test sandbox to confirm nested path.Manual reproduction:
nemoclaw onboard --non-interactive --yes-i-accept-third-party-softwarebash scripts/backup-workspace.sh backup <sandbox-name>→ Reports
Backup saved to ~/.nemoclaw/backups/<ts>/ (6 items)nemoclaw <sandbox-name> destroy --yesnemoclaw onboard --non-interactive --yes-i-accept-third-party-softwarebash scripts/backup-workspace.sh restore <sandbox-name>→ Reports
Restored 6 items to sandbox <sandbox-name>✓ (claims success)→ Returns
/sandbox/.openclaw/workspace/memory/memory/2026-05-15.md(nested) ❌→ Expected:
/sandbox/.openclaw/workspace/memory/2026-05-15.mdEnvironment
OS: Ubuntu 24.04.4 LTS (Linux x86_64)
Node.js: v22.22.x (via nvm)
Docker: Docker Engine 27.x
NemoClaw: v0.0.41
OpenShell: 0.0.39
Debug Output
Logs
Checklist