mastra-ai/mastra · error

Diagnostics output directory is unavailable.

Error message

Diagnostics output directory is unavailable.

What it means

takeSample throws this when the output directory or start timestamp is missing, meaning sampling was invoked before createArtifacts() ran or after cleanup nulls outputDirectory. It guards the invariant that samples can only be written into an initialized run directory.

Source

Thrown at mastracode/sdk/src/process-memory-diagnostics.ts:532

        });
      }
    });
    observer.observe({ entryTypes: ['gc'] });
    this.observer = observer;
  }

  private async startSampling(): Promise<void> {
    if (!this.inspector) throw new Error('Inspector session is unavailable.');
    await this.inspector.post('HeapProfiler.startSampling', {
      samplingInterval: this.config.allocationIntervalBytes,
      includeObjectsCollectedByMajorGC: true,
      includeObjectsCollectedByMinorGC: true,
    });
    this.samplingActive = true;
  }

  private async takeSample(): Promise<ProcessMemoryDiagnosticsMemorySample> {
    if (!this.outputDirectory || !this.startedAt) throw new Error('Diagnostics output directory is unavailable.');
    const sample: ProcessMemoryDiagnosticsMemorySample = {
      timestamp: this.now().toISOString(),
      sequence: this.sampleCount + 1,
      elapsedMs: Math.max(0, this.now().getTime() - this.startedAt.getTime()),
      memory: process.memoryUsage(),
      resourceUsage: process.resourceUsage(),
      heap: getHeapStatistics(),
      heapSpaces: getHeapSpaceStatistics(),
    };
    this.sampleCount = sample.sequence;
    this.latestSample = sample;
    await this.enqueueWrite('process-samples.jsonl', sample);
    const gcEvents = this.pendingGcEvents.splice(0);
    if (gcEvents.length > 0) await this.enqueueWriteBatch('gc-events.jsonl', gcEvents);
    return sample;
  }

  private enqueueWrite(fileName: string, value: unknown): Promise<void> {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Only call start()/stop() sequentially; await start() before stop() and avoid re-entrant calls on the same instance
  2. Check diagnostics.getStatus().state — if 'error' or 'inactive' with null outputDirectory, do not expect samples; create a new ProcessMemoryDiagnostics
  3. If start failed, fix the underlying artifact-creation error (parent directory permissions/disk) before retrying

Example fix

// before
const d = new ProcessMemoryDiagnostics(config);
await d.stop(); // stop-while-starting race on a failed start
// after
const status = await d.start();
if (status.state === 'active') await d.stop();
Defensive patterns

Strategy: validation

Validate before calling

const status = diagnostics.getStatus();
if (status.outputDirectory === null || status.state !== 'active') {
  throw new Error('Diagnostics not initialized; call start() and await an active state before sampling.');
}

Type guard

function isActiveWithOutput(
  status: ProcessMemoryDiagnosticsStatus,
): status is ProcessMemoryDiagnosticsStatus & { outputDirectory: string } {
  return status.state === 'active' && status.outputDirectory !== null;
}

Try / catch

try {
  const sample = await diagnostics.start();
} catch (error) {
  if (error.message === 'Diagnostics output directory is unavailable.') {
    console.error('Start failed or was torn down; fix artifact directory issues and retry start().');
  } else throw error;
}

Prevention

When it happens

Trigger: stop() calls takeSample() when outputDirectory is null (start failed early or cleanup already ran), or a queued sample fires after cleanupAfterStartFailure removed the directory; also if startedAt was never set.

Common situations: Calling stop() while diagnostics are in 'error' state after a failed start, racing stop() against start(), or artifact cleanup removing the directory while an interval callback is mid-flight.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/f954e48b05aea3eb. Report an issue: GitHub.