TencentCloud/TencentDB-Agent-Memory · error
Source not found: ${sourceKey}
Error message
Source not found: ${sourceKey} What it means
ScopedStorageAdapter.rename implements rename as a non-atomic get → put → delete. It throws this error when the source object does not exist in the backend. The code comments note a native renameObject on IStorageBackend is required for true atomicity (tracked long-term, audit H-6).
Source
Thrown at MemoryCore/src/core/storage/adapter.ts:216
return {
key,
size: obj.size ?? obj.content.length,
lastModified,
createdAt: lastModified,
};
}
// ── fs.rename replacement ──
async rename(sourceKey: string, destKey: string): Promise<void> {
// CR-8 partial fix (2026-05-19): preserve contentType + metadata across rename.
// The 3-step (get → put → delete) is still NOT atomic; if the process is killed
// between put and delete, both source and dest will exist (data duplication).
// A complete fix requires a native renameObject in IStorageBackend (using
// POSIX fs.rename for local + COS x-cos-copy-source for remote). Tracked as
// long-term work — see audit report H-6 (persona.md backup rotation).
const obj = await this.backend.getObject(sourceKey);
if (!obj) throw new Error(`Source not found: ${sourceKey}`);
await this.backend.putObject(destKey, obj.content, {
contentType: obj.contentType,
metadata: obj.metadata,
});
await this.backend.deleteObject(sourceKey);
}
// ── fs.copyFile replacement ──
async copyFile(sourceKey: string, destKey: string): Promise<void> {
// CR-8 partial fix (2026-05-19): preserve contentType + metadata across copy.
const obj = await this.backend.getObject(sourceKey);
if (!obj) throw new Error(`Source not found: ${sourceKey}`);
await this.backend.putObject(destKey, obj.content, {
contentType: obj.contentType,
metadata: obj.metadata,
});
}View on GitHub (pinned to 3efcd317b8)
Solutions
- Ensure the source object exists before renaming (write it first or check exists())
- If the intent is 'create or replace', use putObject on destKey instead of rename
- Guard with exists() and skip or create the source as appropriate
- Serialize concurrent operations touching the same key to avoid a delete/rename race
Example fix
// before await adapter.rename(tmpKey, finalKey); // throws if tmpKey missing // after if (await adapter.exists(tmpKey)) await adapter.rename(tmpKey, finalKey); else await adapter.putObject(finalKey, defaultContent);
Defensive patterns
Strategy: validation
Validate before calling
if (!(await adapter.exists(sourceKey))) {
throw new NotFoundError(`cannot rename missing object: ${sourceKey}`);
}
await adapter.rename(sourceKey, destKey); Try / catch
try {
await adapter.rename(src, dst);
} catch (e) {
if (String(e.message).startsWith('Source not found:')) {
await adapter.putObject(dst, defaultContent); // create-or-replace semantics
} else throw e;
} Prevention
- Remember rename is get→put→delete, not atomic — avoid concurrent renames of the same key
- For 'replace' semantics use putObject directly instead of rename
- For 'move' semantics check existence first and define absent-source behavior
- Write the temp file immediately before renaming in write-temp patterns
When it happens
Trigger: Calling rename(sourceKey, destKey) — directly or indirectly via atomicWriteJson — when getObject(sourceKey) returns null, i.e. the source object was never written or was already deleted.
Common situations: atomicWriteJson on a path whose current file doesn't exist yet when expecting one; racing deletes from another worker; key mismatch (case, prefix, extension) between write and rename.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- File not found: ${key}
- STORAGE_NOT_FOUND
- Generation log object key exceeds COS limit
- Invalid generation log key
- Invalid generation log cursor
AI-assisted analysis of TencentCloud/TencentDB-Agent-Memory@3efcd317b8 (2026-09-01).
Data as JSON: /api/errors/a5c9078504931561.
Report an issue: GitHub.