{"record":{"id":"8896ff71ef777645","repo":"mastra-ai/mastra","slug":"invalid-checkpoint-name-name","errorCode":null,"errorMessage":"Invalid checkpoint name: ${name}","messagePattern":"Invalid checkpoint name: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/workspace/sandbox/local-sandbox.ts","lineNumber":405,"sourceCode":"        this._seatbeltProfilePath = path.join(this._sandboxFolderPath, `seatbelt-${configHash}.sb`);\n        await fs.writeFile(this._seatbeltProfilePath, generatedProfile, 'utf-8');\n      }\n    }\n\n    this.logger.debug('Sandbox started', { workingDirectory: this.workingDirectory });\n  }\n\n  // ---------------------------------------------------------------------------\n  // Checkpoints\n  // ---------------------------------------------------------------------------\n\n  /** LocalSandbox persists real filesystem-backed checkpoints. */\n  readonly supportsCheckpoints = true;\n\n  /** Resolve the on-disk directory for a named checkpoint, rejecting unsafe names. */\n  private _checkpointPath(name: string): string {\n    if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) || name.includes('..')) {\n      throw new Error(`Invalid checkpoint name: ${name}`);\n    }\n    return path.join(this._checkpointsDirectory, name);\n  }\n\n  /**\n   * Seed an empty/missing working directory from the configured checkpoint.\n   * Missing checkpoint or already-populated workdir → no-op (normal start).\n   */\n  private async _seedFromCheckpoint(): Promise<void> {\n    if (!this._checkpointName && !this._seedCheckpointName) return;\n\n    // Only seed an empty working directory; a populated one wins.\n    const entries = await fs.readdir(this.workingDirectory).catch(() => []);\n    if (entries.length > 0) return;\n\n    // Prefer the primary checkpoint; fall back to the boot-only seed checkpoint.\n    const candidates = [this._checkpointName, this._seedCheckpointName].filter(\n      (name): name is string => name !== undefined,","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workspace/sandbox/local-sandbox.ts#L387-L423","documentation":"LocalSandbox._checkpointPath() resolves the on-disk directory for a named checkpoint and rejects unsafe names. A valid name starts with an alphanumeric character and contains only [A-Za-z0-9._-], and may not contain `..` anywhere. This prevents path traversal via checkpoint names when they're joined into the checkpoints directory.","triggerScenarios":"Calling checkpoint-related APIs (checkpointDir, target, restore flows) with a name like `../../etc`, `my checkpoint` (space), `#draft`, an empty string, or any name with slashes/unicode.","commonSituations":"Generating checkpoint names from user input, branch names, or timestamps containing `/` or spaces; using identifiers with `#` or `:`; forgotten sanitization when checkpoint names come from API consumers.","solutions":["Sanitize the name to the allowed pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/ (no slashes, spaces, or `..`)","Derive names programmatically, e.g. slugify or Date.now()-based names like `cp-1724912345`","Validate before calling any checkpoint API and show a clear message to the user when their input is invalid"],"exampleFix":"// before\nsandbox.checkpointDir(featureBranchName); // 'feat/my-branch' throws\n// after\nconst safeName = 'cp-' + featureBranchName.replace(/[^A-Za-z0-9._-]/g, '-').replace(/^[^A-Za-z0-9]/, 'x');\nsandbox.checkpointDir(safeName);","handlingStrategy":"validation","validationCode":"export function toSafeCheckpointName(raw: string): string {\n  const name = raw.replace(/[^A-Za-z0-9._-]/g, '-').replace(/\\.+/g, '.').replace(/^[^A-Za-z0-9]/, 'x');\n  if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) || name.includes('..')) throw new Error(`Invalid checkpoint name: ${raw}`);\n  return name;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const dir = sandbox.checkpointDir(name);\n} catch (err) {\n  if (/Invalid checkpoint name/.test(String(err?.message))) {\n    throw new Error(`Checkpoint names must match [A-Za-z0-9][A-Za-z0-9._-]*; got: ${name}`);\n  }\n  throw err;\n}","preventionTips":["Generate checkpoint names programmatically (timestamps/uuids) instead of from free-form user input","Slugify branch or feature names before using them as checkpoint names","Share one sanitizing helper across all checkpoint call sites so the pattern is enforced once"],"tags":["validation","security","path-traversal","checkpoint"],"backgroundTag":"invalid-identifier-name","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}