{"record":{"id":"187dd74d13c61b58","repo":"mastra-ai/mastra","slug":"directory-not-found-in-skill-version-tree-path","errorCode":null,"errorMessage":"Directory not found in skill version tree: ${path}","messagePattern":"Directory not found in skill version tree: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/workspace/skills/versioned-skill-source.ts","lineNumber":131,"sourceCode":"\n    const blob = await this.#blobStore.get(entry.blobHash);\n    if (!blob) {\n      throw new Error(`Blob not found for hash ${entry.blobHash} (file: ${path})`);\n    }\n\n    // Decode base64-encoded binary content back to Buffer\n    if (entry.encoding === 'base64') {\n      return Buffer.from(blob.content, 'base64');\n    }\n\n    return blob.content;\n  }\n\n  async readdir(path: string): Promise<SkillSourceEntry[]> {\n    const normalized = this.#normalizePath(path);\n\n    if (!this.#directories.has(normalized)) {\n      throw new Error(`Directory not found in skill version tree: ${path}`);\n    }\n\n    const prefix = normalized === '' ? '' : normalized + '/';\n    const seen = new Set<string>();\n    const entries: SkillSourceEntry[] = [];\n\n    for (const filePath of Object.keys(this.#tree.entries)) {\n      if (!filePath.startsWith(prefix)) continue;\n\n      // Get the next segment after the prefix\n      const remaining = filePath.slice(prefix.length);\n      const nextSegment = remaining.split('/')[0];\n      if (!nextSegment || seen.has(nextSegment)) continue;\n      seen.add(nextSegment);\n\n      // If there's more after the next segment, it's a directory\n      const isDirectory = remaining.includes('/');\n      entries.push({","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workspace/skills/versioned-skill-source.ts#L113-L149","documentation":"VersionedSkillSource.readdir validates the normalized path against the #directories set, which is computed from the parent directories implied by the tree's file entries. If the path is not such an implied directory (including the root '' or '.'), the directory does not exist in this version and the error is thrown.","triggerScenarios":"Calling readdir with a path that is a file, a directory that contains no files in this version, an empty-but-expected folder never committed with files, or a path outside the skill root.","commonSituations":"Scanning an 'assets/' folder that exists live but had no files in the published version; listing a directory added in a newer version; passing an absolute or skill-prefixed path to a per-skill source.","solutions":["Check the directory exists with exists(path) before readdir.","List the root ('' or '.') first to see the actual structure of this version.","Confirm the version tree actually contains files under that directory (directories are derived from file paths only).","Republish if the directory should contain files in this version."],"exampleFix":"// before\nawait versioned.readdir('assets'); // empty dir, no entries in tree\n// after\nawait versioned.readdir(''); // list skill root","handlingStrategy":"validation","validationCode":"if (!(await versioned.exists(dir))) {\n  return [];\n}\nconst entries = await versioned.readdir(dir);","typeGuard":null,"tryCatchPattern":"try {\n  return await versioned.readdir(dir);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Directory not found in skill version tree')) {\n    return [];\n  }\n  throw err;\n}","preventionTips":["Only expect directories that contain at least one file in the tree (dirs are derived from file paths).","Use '' or '.' to list the skill root.","Strip any skill-directory prefix before calling per-skill sources."],"tags":["filesystem","skills","versioning","not-found"],"backgroundTag":"directory-not-found","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}