mastra-ai/mastra · error · ThreadLockError
ThreadLockError(threadId, ownerPid)
Error message
ThreadLockError(threadId, ownerPid)
What it means
ThreadLockError is thrown by acquireThreadLock when a lock file for the thread already exists and the PID inside it belongs to another process that is still alive. This prevents two sessions from mutating the same thread concurrently. Stale locks (dead owner) or the caller's own PID are safely reclaimed instead.
Source
Thrown at mastracode/sdk/src/utils/thread-lock.ts:61
}
/**
* Attempt to acquire a lock for the given thread.
* Throws ThreadLockError if another live process holds the lock.
* Reclaims stale locks from dead processes.
*/
export function acquireThreadLock(threadId: string): void {
const lockPath = getLockPath(threadId);
const myPid = process.pid;
// Check for existing lock
if (fs.existsSync(lockPath)) {
try {
const content = fs.readFileSync(lockPath, 'utf-8').trim();
const ownerPid = parseInt(content, 10);
if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {
throw new ThreadLockError(threadId, ownerPid);
}
// Stale lock (dead process) or our own lock — reclaim it
} catch (error) {
if (error instanceof ThreadLockError) throw error;
// File read error — try to overwrite
}
}
// Write our PID to the lock file
fs.writeFileSync(lockPath, String(myPid), { mode: 0o644 });
}
/**
* Release the lock for the given thread (only if we own it).
*/
export function releaseThreadLock(threadId: string): void {
const lockPath = getLockPath(threadId);
const myPid = process.pid;View on GitHub (pinned to 75dd419e61)
Solutions
- Identify the owning process (`ps -p <ownerPid>`) and terminate it or close the other session; the lock is then reclaimed automatically on next acquire
- If the PID is dead but isProcessAlive misjudges (containers/VMs with different PID namespaces), delete the lock file manually
- Avoid running two mastracode sessions against the same thread in parallel
Example fix
// recover from a stale lock whose owner is gone ps -p 42421 || rm .mastra/locks/<threadId>.lock
Defensive patterns
Strategy: try-catch
Validate before calling
import { existsSync, readFileSync } from 'node:fs';
import { process } from 'node:process';
const lockPath = getLockPath(threadId); // your lock path helper
if (existsSync(lockPath)) {
const pid = parseInt(readFileSync(lockPath, 'utf-8').trim(), 10);
if (!isNaN(pid) && pid !== process.pid && isAlive(pid)) {
throw new Error(`Thread ${threadId} is locked by PID ${pid}; close that session first.`);
}
} Type guard
const isLockHeldByLiveProcess = (lockPath: string, myPid: number): boolean => {
try {
const pid = parseInt(readFileSync(lockPath, 'utf-8').trim(), 10);
return !isNaN(pid) && pid !== myPid;
} catch { return false; }
}; Try / catch
try {
await acquireThreadLock(threadId);
} catch (e) {
if (e instanceof ThreadLockError) {
console.error(`Thread "${e.threadId}" is locked by PID ${e.ownerPid}. Close that session or kill the process.`);
} else throw e;
} Prevention
- Don't run two mastracode sessions against the same thread in parallel
- If a PID is reported dead but the lock persists, remove the lock file manually
- Check `ps -p <ownerPid>` before deleting a lock to avoid stealing a live session's lock
- Be careful with PID namespaces in containers when judging staleness
When it happens
Trigger: Two mastracode processes try to acquire the lock for the same threadId; the lock file at the thread's lock path contains a live foreign PID (verified with isProcessAlive).
Common situations: The same project/thread opened in two terminals; a previously 'killed' session that actually survived (detached child, background job) still holds the lock; PID reuse coincidentally matching a stale lock file's PID.
Related errors
- MastraFactory.prepare() called twice
- Factory kickoff lease was lost before completion.
- Failed to spawn LSP server
- Failed to create LSP process with proper stdio
- ${file} is in use by another process — is another Mastra Cod
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/7a06695f4a1f3be4.
Report an issue: GitHub.