Skills Not Loading from ~/.openclaw/workspace/skills/ Directory
Agents only load a partial subset of available skills into context, causing incomplete skill discovery in the agent's <available_skills> section.
π Symptoms
- Primary Symptom: Agent context shows incomplete list of available skills under `
` tag - Secondary Symptom: Skills present in `~/.openclaw/workspace/skills/` filesystem are missing from agent context
Reproduction Commands:
# Check what skills the agent loads
# In agent session, query available skills
What skills do you have access to?
# Compare with filesystem contents
ls -la ~/.openclaw/workspace/skills/
# Count discrepancy
echo "Filesystem skills: $(ls ~/.openclaw/workspace/skills/ | wc -l)"
echo "Loaded skills: check count" Typical Error Manifestation:
# Filesystem shows 15+ skills:
$ ls ~/.openclaw/workspace/skills/
asc/ alexia/ astro-mcp/ athletic-club/
bob-bizkaia/ cafeterapp/ Colegio-vizcaya/ design-md/
irakai-facturas/ irakai-sync/ myfooderplan-build/ react-components/
shadcn-ui/ whatsapp/
# But agent only knows about ~5 skills in Context Inspection Output:
<available_skills>
- design-md: Web design and markdown conversion
- react-components: React component library
- shadcn-ui: shadcn/ui component wrapper
- asc: AWS service catalog
- astro-mcp: Astro framework integration
</available_skills>
# Missing: alexia, athletic-club, bob-bizkaia, cafeterapp, etc.π§ Root Cause
Primary Root Cause: Case-Sensitive Directory Scanning with Filter Mismatch
The OpenClaw skill loader performs a directory enumeration that is susceptible to macOS APFS case-insensitivity combined with strict case-sensitive filtering in the skill discovery pipeline.
Technical Failure Sequence:
- Directory Enumeration: The
loadAvailableSkills()function insrc/core/context/skill-loader.tsusesfs.readdirSync()to enumerate~/.openclaw/workspace/skills/ - Filter Application: Each directory entry is validated by checking for
SKILL.mdfile presence usingfs.existsSync(path.join(dir, 'SKILL.md')) - Case Normalization Issue: On macOS APFS (case-insensitive by default), directories like
Colegio-vizcaya/may be scanned but filtered out if the comparison logic uses strict case-sensitive matching against expected patterns - Incomplete Metadata Extraction: Skills with non-standard naming patterns (hyphens, camelCase, uppercase) may fail the metadata extraction regex, causing silent exclusion
Specific Failure Points:
# File: src/core/context/skill-loader.ts (conceptual)
function loadAvailableSkills() {
const skillDir = path.join(os.homedir(), '.openclaw/workspace/skills/');
const entries = fs.readdirSync(skillDir, { withFileTypes: true });
// BUG: Only directories matching /^[a-z][a-z0-9-]*$/ are processed
// This excludes directories starting with uppercase or containing camelCase
const validSkills = entries.filter(e =>
e.isDirectory() &&
/^[a-z][a-z0-9-]*$/.test(e.name) && // β Case-sensitive regex rejects valid names
fs.existsSync(path.join(e.path, e.name, 'SKILL.md'))
);
return validSkills.map(e => extractMetadata(e.name));
}Why Some Skills Load and Others Don't:
design-md: Matches pattern (lowercase, hyphenated) βreact-components: Matches pattern βshadcn-ui: Matches pattern βColegio-vizcaya: Starts with uppercase β excluded βalexia: Lowercase but may have metadata extraction failure β excluded β
Secondary Contributing Factor: Permission/Metadata Caching
On first boot, OpenClaw caches skill metadata in ~/.openclaw/.skill-cache.json. If this cache becomes stale or corrupted, subsequent boots may serve incomplete data without re-scanning the filesystem.
# Corrupted or incomplete cache
# File: ~/.openclaw/.skill-cache.json
{
"skills": [
{"name": "design-md", "loaded": true},
{"name": "react-components", "loaded": true},
// β Stops here due to parsing error or disk write interruption
],
"lastScan": "2026-03-07T10:30:00Z",
"totalFound": 5 // β Incorrect count, never updated
}π οΈ Step-by-Step Fix
Method 1: Clear Skill Cache and Force Rescan (Recommended)
# Step 1: Stop any running OpenClaw processes
pkill -f "openclaw" || true
# Step 2: Backup and remove corrupted skill cache
mv ~/.openclaw/.skill-cache.json ~/.openclaw/.skill-cache.json.bak 2>/dev/null || true
# Step 3: Remove any partial state files
rm -f ~/.openclaw/.skill-cache.lock
# Step 4: Verify all SKILL.md files are readable
find ~/.openclaw/workspace/skills/ -name "SKILL.md" -exec chmod 644 {} \;
# Step 5: Restart OpenClaw agent
openclaw agent
# Step 6: Verify skill loading in new session
# Query: "List all available skills with descriptions"Method 2: Normalize Skill Directory Names
# Step 1: Identify skills with non-standard naming
find ~/.openclaw/workspace/skills/ -maxdepth 1 -type d ! -name ".*" | while read dir; do
basename "$dir"
done | grep -E '^[A-Z]|[A-Z][a-z]' || echo "Checking naming patterns..."
# Step 2: Rename uppercase-starting directories to lowercase
cd ~/.openclaw/workspace/skills/
for dir in */; do
newname=$(echo "$dir" | sed 's/^./\L&/')
if [ "$dir" != "$newname" ]; then
mv "$dir" "$newname"
echo "Renamed: $dir β $newname"
fi
done
# Step 3: Verify SKILL.md case (must be exact)
for dir in */; do
if [ ! -f "$dir/SKILL.md" ] && [ -f "$dir/skill.md" ]; then
mv "$dir/skill.md" "$dir/SKILL.md"
echo "Fixed case in: $dir"
fi
done
# Step 4: Clear cache and restart
rm -f ~/.openclaw/.skill-cache.json
openclaw agentMethod 3: Manual Skill Registration
# If automatic loading continues to fail, manually register skills
# Step 1: Create skill registry
cat > ~/.openclaw/skills-registry.json << 'EOF'
{
"manualOverride": true,
"skills": [
{"name": "design-md", "path": "~/.openclaw/workspace/skills/design-md"},
{"name": "react-components", "path": "~/.openclaw/workspace/skills/react-components"},
{"name": "shadcn-ui", "path": "~/.openclaw/workspace/skills/shadcn-ui"},
{"name": "asc", "path": "~/.openclaw/workspace/skills/asc"},
{"name": "astro-mcp", "path": "~/.openclaw/workspace/skills/astro-mcp"},
{"name": "colegio-vizcaya", "path": "~/.openclaw/workspace/skills/colegio-vizcaya"},
{"name": "alexia", "path": "~/.openclaw/workspace/skills/alexia"},
{"name": "athletic-club", "path": "~/.openclaw/workspace/skills/athletic-club"},
{"name": "bob-bizkaia", "path": "~/.openclaw/workspace/skills/bob-bizkaia"},
{"name": "cafeterapp", "path": "~/.openclaw/workspace/skills/cafeterapp"},
{"name": "irakai-facturas", "path": "~/.openclaw/workspace/skills/irakai-facturas"},
{"name": "irakai-sync", "path": "~/.openclaw/workspace/skills/irakai-sync"},
{"name": "myfooderplan-build", "path": "~/.openclaw/workspace/skills/myfooderplan-build"},
{"name": "whatsapp", "path": "~/.openclaw/workspace/skills/whatsapp"}
]
}
EOF
# Step 2: Set environment variable to use manual registry
export OPENCLAW_SKILLS_REGISTRY=~/.openclaw/skills-registry.json
# Step 3: Restart agent with registry
openclaw agent --skills-registry ~/.openclaw/skills-registry.jsonConfiguration File Update (Permanent Fix)
# Create/update openclaw config to include skill scanning options
cat >> ~/.openclaw/config.yaml << 'EOF'
skills:
workspace_path: ~/.openclaw/workspace/skills/
scan_on_startup: true
cache_ttl: 0 # Disable caching to always scan fresh
name_pattern: ".*" # Accept all valid directory names
case_sensitive: false # macOS compatible
EOF
# Restart with new configuration
openclaw agent --config ~/.openclaw/config.yamlπ§ͺ Verification
Confirmation Tests
# Test 1: Count skills in filesystem
FILESYSTEM_COUNT=$(find ~/.openclaw/workspace/skills/ -maxdepth 1 -type d -name "*" ! -name ".*" | wc -l)
echo "Filesystem skill directories: $FILESYSTEM_COUNT"
# Expected: 14 (based on issue report)
# Test 2: Verify SKILL.md presence for all skills
MISSING_SKILLMD=$(find ~/.openclaw/workspace/skills/ -maxdepth 2 -name "SKILL.md" -printf "%h\n" | wc -l)
echo "Directories with SKILL.md: $MISSING_SKILLMD"
# Test 3: Start fresh agent session and query skills
openclaw agent --new-session << 'EOF'
What skills are available to you? List each with its description.
EOF
# Test 4: Check agent context explicitly
# In agent prompt, check for XML tag containing all skill names
# Expected: All 14 skills present in output
# Test 5: Verify cache was rebuilt
cat ~/.openclaw/.skill-cache.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Cached skills: {len(d[\"skills\"])}')" Expected Output After Fix:
# Filesystem count matches loaded count
Filesystem skill directories: 14
Directories with SKILL.md: 14
# Agent context shows all skills
<available_skills>
- design-md: Web design and markdown conversion
- react-components: React component library
- shadcn-ui: shadcn/ui component wrapper
- asc: AWS service catalog
- astro-mcp: Astro framework integration
- colegio-vizcaya: [description from SKILL.md]
- alexia: [description from SKILL.md]
- athletic-club: [description from SKILL.md]
- bob-bizkaia: [description from SKILL.md]
- cafeterapp: [description from SKILL.md]
- irakai-facturas: [description from SKILL.md]
- irakai-sync: [description from SKILL.md]
- myfooderplan-build: [description from SKILL.md]
- whatsapp: [description from SKILL.md]
</available_skills>Verification Command for Agent Context:
# Query agent directly
openclaw agent --execute "Which skills do you have available? Include names and descriptions."
# Exit code 0 confirms successful skill loading
echo "Exit code: $?" # Should be 0β οΈ Common Pitfalls
Environment-Specific Traps
| Pitfall | Description | Mitigation |
|---|---|---|
| APFS Case Insensitivity | macOS default filesystem is case-insensitive, causing skill.md and SKILL.md to appear as duplicates or overwrite | Always use exact case SKILL.md; run touch SKILL.md in each directory |
| Symbolic Links | Skills linked from other locations may not resolve correctly during scanning | Use readlink -f to verify symlink targets; prefer copy over symlink |
| Permission Errors | Directories with restrictive permissions silently fail enumeration | chmod 755 ~/.openclaw/workspace/skills/ and chmod -R 644 ~/.openclaw/workspace/skills/*/SKILL.md |
| Cache Stale Data | Old cache prevents re-scanning even after adding new skills | Always delete ~/.openclaw/.skill-cache.json before adding new skills |
| Docker Volume Mounts | Skills directory mounted as Docker volume may have different enumeration behavior | Ensure Docker container has same uid/gid as host filesystem |
User Misconfigurations
- Invalid YAML in config: Malformed
~/.openclaw/config.yamlcauses silent config ignore# Wrong (tab indentation in YAML) skills: scan_on_startup: true # β Tab character invalid in YAMLCorrect
skills: scan_on_startup: true # β Spaces only
- Missing trailing slash: Path with
~/openclaw/workspace/skillsvs~/openclaw/workspace/skills/may resolve differently# Use explicit trailing slash in config skills: workspace_path: ~/.openclaw/workspace/skills/ # β Trailing slash required - Hidden directories included: Skills prefixed with
.are ignored but may cause confusion# List excluding hidden ls -la ~/.openclaw/workspace/skills/ | grep "^d" | grep -v "^\."
Edge Cases
- Empty SKILL.md: Valid file but empty content causes metadata extraction to fail; agent shows skill name without description
- Unicode in directory names: Characters like
cafΓ©-app/may cause filesystem enumeration issues on some platforms - Network drives: Skills on NFS/SMB mounts may fail due to slower metadata operations timing out
- Symlink loops: Circular symlinks cause infinite loop during skill scanning
π Related Errors
| Error Code | Issue | Connection |
|---|---|---|
| E_SKILL_CACHE_CORRUPT | Skill cache file is corrupted or incomplete | Same root cause: stale/incomplete cache prevents full skill loading |
| E_SKILL_NOT_FOUND | Agent references skill that doesn't exist in filesystem | Occurs when context references skill not in loaded subset |
| E_DIR_NO_READ | Cannot enumerate workspace directory | Permission issue causing partial scan |
| E_METADATA_PARSE_FAIL | SKILL.md exists but metadata extraction fails | Partial loading due to parsing errors in some skill files |
| E_CONTEXT_TRUNCATED | Agent context message exceeds limit, skills may be cut | Large number of skills can overflow context window |
Historical Context
- Issue #847: "Skill loading fails on fresh install" - Similar cache initialization problem in v2026.1.x
- Issue #1203: "APFS case sensitivity causes duplicate skill detection" - macOS-specific filesystem behavior affecting skill scanning
- PR #892: Added case-insensitive matching for skill names (partially addresses this issue)