Skip to content

[BUG] WorkspaceSkills splits absolute paths on '/' only — breaks on Windows #17670

Description

@stephen-carter-at-sf

Describe the Bug

WorkspaceSkills in @mastra/core hardcodes '/' as the path separator in five places, so absolute Windows paths (e.g. C:\Users\me\skills\my-skill) passed via new Workspace({ skills: [...] }) are mishandled on Windows. Each call site fails differently:

Line Method Code Effect on Windows
316 addSkill this.#getParentPath(skillPath).split('/').pop() || 'unknown' dirName is 'unknown' (#getParentPath uses lastIndexOf('/') and returns '/' on a pure-backslash path → '/'.split('/').pop() === '' → fallback)
319 addSkill skillPath.split('/').pop() || 'unknown' dirName is the entire path string (no / found, .split('/').pop() returns the whole input)
773 #discoverDirectSkill skillDir.split('/').pop() || skillDir same — dirName is the entire path string
791 #discoverDirectSkill skillsPath.split('/').pop() || skillsPath same
1217 #determineSource const segments = skillsPath.split('/'); if (segments.includes('node_modules')) ... node_modules segment never detected on a pure-backslash path → external skills mis-classified as 'local'

These are all in packages/core/src/workspace/skills/workspace-skills.ts. Same bug present on main and on every published 1.x (verified against 1.36.0 and 1.41.0).

Steps To Reproduce

The bug is pure string handling — the inputs are Windows-shaped path strings, so the four failures can be observed on any OS without a Windows machine. The repro transcribes the offending functions verbatim from upstream and runs them against three realistic Windows path inputs.

git clone https://gist.github.com/stephen-carter-at-sf/d6a6303218278b64181c9cc8fb95ae26 mastra-skill-windows-bug
cd mastra-skill-windows-bug
node repro.mjs

Output (current behavior, exit code 1):

FAIL: addSkill dirName (directory path)
        expected: "my-skill"
        actual:   "C:\\Users\\me\\skills\\my-skill"
FAIL: addSkill dirName (SKILL.md path)
        expected: "my-skill"
        actual:   "unknown"
FAIL: #discoverDirectSkill dirName (directory case)
        expected: "my-skill"
        actual:   "C:\\Users\\me\\skills\\my-skill"
FAIL: #determineSource node_modules detection
        expected: "external"
        actual:   "local"

4 of 4 cases fail — see workspace-skills.ts lines 316, 319, 773, 791, 1217.

In a real Mastra agent on Windows, the user-visible symptom is: skills configured by absolute path either fail to load or load with the wrong name (the entire path string, or 'unknown'), so Skill.get('my-skill') returns nothing and search/load tools can't find them.

Link to Minimal Reproducible Example

https://gist.github.com/stephen-carter-at-sf/d6a6303218278b64181c9cc8fb95ae26

Expected Behavior

new Workspace({ skills: ['C:\\Users\\me\\skills\\my-skill'] }) on Windows should discover the skill with name = 'my-skill', identical to the macOS/Linux behavior with '/Users/me/skills/my-skill'. #determineSource should classify a path containing node_modules as 'external' regardless of separator.

Suggested fix

Workspace-internal paths can stay POSIX (the docstring on #joinPath already notes "workspace paths use forward slashes"). The only places that need separator-tolerance are the five entry-point lines above where consumer-supplied absolute paths land. A small helper covers all five:

function splitPathSegments(p: string): string[] {
    return p.split(/[\\/]+/);
}

…and #getParentPath should use Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\')) instead of just '/'.

Happy to send a PR if that helps.

Environment Information

Bug is in source — reproducible on any OS by running the script above against published `@mastra/core@1.41.0` and the current `main` branch. No environment-specific observation needed; pasting `npx envinfo` output isn't load-bearing for a string-handling bug, but for completeness:

  System: macOS 25.5.0
  @mastra/core versions verified: 1.36.0, 1.41.0, main (HEAD)

LLM Provider: N/A (the failing code path runs before any model is invoked)

Verification

  • I have searched the existing issues to make sure this is not a duplicate
  • I have included sufficient information for the team to reproduce and understand the issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions