n8n-io/n8n · error

Filesystem "${this.id}" is not ready (status: ${this.status}

Error message

Filesystem "${this.id}" is not ready (status: ${this.status})

What it means

BaseFilesystem.ensureReady() is the gate every filesystem operation passes through (via withFs). If status is not 'ready' it calls _init(); if status is STILL not 'ready' afterwards, the adapter is unusable and the call would hit an uninitialized backend, so it throws with the observed status rather than proceed silently. This is a lifecycle/state-machine invariant violation.

Source

Thrown at packages/@n8n/agents/src/workspace/filesystem/base-filesystem.ts:92

				// Non-fatal: bad callback shouldn't kill a healthy filesystem
			}
		} catch (error) {
			this.status = 'error';
			this.error = error instanceof Error ? error.message : String(error);
			throw error;
		}
	}

	async init(): Promise<void> {
		// Default no-op — subclasses override
	}

	protected async ensureReady(): Promise<void> {
		if (this.status !== 'ready') {
			await this._init();
		}
		if (this.status !== 'ready') {
			throw new Error(`Filesystem "${this.id}" is not ready (status: ${this.status})`);
		}
	}

	async _destroy(): Promise<void> {
		if (this.status === 'destroyed') return;

		if (this.status === 'pending') {
			this.status = 'destroyed';
			return;
		}

		if (this.destroyPromise) return await this.destroyPromise;

		this.destroyPromise = this.executeDestroy();
		try {
			await this.destroyPromise;
		} finally {
			this.destroyPromise = undefined;

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Read the status in the error text: 'destroyed' means create a new workspace — the object is not recoverable; 'pending'/'error' usually means init failed, so inspect the underlying init error.
  2. Ensure any subclass _init() sets `this.status = 'ready'` on success and rethrows on failure (don't swallow).
  3. For transient provider failures, recreate the sandbox + filesystem and retry the op rather than reusing the object.

Example fix

// before (subclass bug)
async _init() {
  await this.client.connect();
  // forgot to set status
}

// after
async _init() {
  await this.client.connect();
  this.status = 'ready';
}
Defensive patterns

Strategy: validation

Validate before calling

function isReady(fs: { status: string }): boolean {
  return fs.status === 'ready';
}
// before a heavy op:
if (!isReady(filesystem)) {
  await filesystem.init?.();
}
if (!isReady(filesystem)) throw new Error('filesystem not ready — recreate workspace');

Try / catch

try {
  await filesystem.readFile(path);
} catch (e) {
  if (e instanceof Error && /is not ready/.test(e.message)) {
    // terminal-ish: recreate sandbox + filesystem, then retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Any filesystem op (readFile, writeFile, stat, list…) invoked when: a subclass _init() threw and left status as 'pending'/'error'; _init() returned without setting status='ready' (subclass bug); the filesystem was concurrently destroyed between the status check and the re-check; _destroy() ran and transitioned status to 'destroyed'.

Common situations: Daytona/E2B auth failed during init but the error was swallowed; the sandbox backing the filesystem was destroyed while an op was queued; a custom BaseFilesystem subclass overrides _init() and forgets `this.status = 'ready'` on success.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/c17f6e2b39dd3aac. Report an issue: GitHub.