Common mistakes15 min read

Custom commands and skills - Common mistakes

SFEIR Institute•

TL;DR

Custom commands, skills and hooks in Claude Code generate considerable productivity gains, but their configuration errors cause silent malfunctions. This guide lists the 10 most frequent pitfalls when setting up custom slash commands, subagents and automations, with concrete fixes for each case.

Custom commands, skills and hooks in Claude Code generate considerable productivity gains, but their configuration errors cause silent malfunctions. This guide lists the 10 most frequent pitfalls when setting up custom slash commands, subagents and automations, with concrete fixes for each case.

Errors related to custom commands and skills in Claude Code represent the leading cause of frustration among developers who automate their workflows. Claude Code v2.1 offers a complete ecosystem - slash commands, skills, hooks and subagents - but each component has its own configuration constraints.

over 40% of support tickets concern syntax or structure errors in customization files. These problems are avoidable if you know the documented pitfalls below.

What are the most frequent errors with custom commands?

Here is an overview of the 10 errors ranked by frequency and severity. Each error is reproducible and fixable in under 5 minutes.

RankErrorSeverityComponent
1Wrong folder structure for slash commandsCriticalCommands
2Overly vague prompt in a skillCriticalSkills
3Hook without error handlingCriticalHooks
4Subagent launched without sufficient contextWarningSubagents
5Name conflict between local and global commandsWarningCommands
6Forgetting the description field in a skillWarningSkills
7Hook blocking on an infinite timeoutCriticalHooks
8Subagent with overly broad permissionsWarningSubagents
9Slash command without argument validationMinorCommands
10Skill that duplicates native behaviorMinorSkills

For an overview of the features before diving into the errors, see the complete custom commands and skills guide which details each component.

Key takeaway: Rank your fixes by severity - critical errors cause silent failures, warnings degrade result quality.

How to avoid the folder structure error for slash commands?

Error 1 - Severity: Critical. Placing command files in the wrong location prevents Claude Code from detecting them.

Claude Code expects your slash commands in the .claude/commands/ directory at the project root. A misplaced file - for example in .claude/slash/ or commands/ - is ignored without an error message. In practice, 30% of developers create the folder at the wrong level.

The expected format is a Markdown file bearing the command name. Each .md file becomes a command invocable by /file-name.

Incorrect:

# Incorrect structure - commands not detected
mkdir -p commands/
echo "My prompt" > commands/review.md
# Or worse: JSON file instead of Markdown
echo '{"prompt": "review"}' > .claude/commands/review.json

Correct:

# Correct structure for project commands
mkdir -p .claude/commands/
echo "Analyze this file and suggest improvements" > .claude/commands/review.md

# Correct structure for global commands (user)
mkdir -p ~/.claude/commands/
echo "Summarize this code in 3 points" > ~/.claude/commands/summarize.md

Concretely, commands in .claude/commands/ are project-specific and shared via Git, while those in ~/.claude/commands/ are global to your machine. You will find details on similar errors in the essential slash commands - common errors guide.

Key takeaway: Always verify that your commands are in .claude/commands/ (project) or ~/.claude/commands/ (global), in Markdown format exclusively.

Why does an overly vague prompt make a skill unusable?

Error 2 - Severity: Critical. A skill is a reusable prompt file that encodes your work patterns. A vague prompt produces inconsistent results.

A skill allows Claude Code to learn your conventions. But if the prompt lacks constraints, the AI interprets freely and produces different results with each invocation. an effective skill contains at minimum 3 explicit constraints and 1 expected output example.

Incorrect:

# .claude/commands/test.md
Write tests for this code.

Correct:

# .claude/commands/test.md
Write unit tests for the file $ARGUMENTS following these rules:
- Framework: Vitest
- Minimum coverage: 80% of branches
- Name tests with the pattern "should [action] when [condition]"
- Mock external dependencies with vi.mock()
- Include at least 1 error case test

Expected output example:
typescript

describe('myFunction', () => { it('should return true when input is valid', () => { expect(myFunction('valid')).toBe(true); }); });

The $ARGUMENTS variable lets you pass dynamic parameters to your command. You type /test src/utils.ts and Claude Code replaces $ARGUMENTS with src/utils.ts. In practice, a well-constrained skill reduces manual corrections by 60%.

To explore writing effective prompts further, see the custom commands tutorial which offers ready-to-use templates.

Key takeaway: Specify the framework, naming conventions, target coverage, and a concrete example in each skill.

How to fix a hook without error handling?

Error 3 - Severity: Critical. A hook is a shell command triggered automatically by a Claude Code event. Without error handling, a failing hook blocks the entire session.

Hooks are deterministic automations - unlike AI-based skills, they execute predictable shell code. A hook configured in .claude/settings.json that fails silently can corrupt your workflow without you noticing.

Incorrect:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "npx prettier --write $CLAUDE_FILE_PATH"
      }
    ]
  }
}

This hook formats every file written by Claude Code, but if Prettier is not installed or if the path contains spaces, the failure goes unnoticed.

Correct:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "if command -v npx >/dev/null 2>&1; then npx prettier --write \"$CLAUDE_FILE_PATH\" || echo 'Prettier failed' >&2; fi"
      }
    ]
  }
}

Concretely, the corrected hook checks that npx exists, wraps the path in quotes, and captures the error. Hooks support the events PreToolUse, PostToolUse, Notification, and Stop. Hook errors share similar patterns with permissions and security errors - a poorly secured command can expose your system.

Hook eventTriggerUse case
PreToolUseBefore each toolValidation, conditional blocking
PostToolUseAfter each toolFormatting, linting, logging
NotificationSystem messageSlack alerts, notifications
StopEnd of sessionCleanup, automatic commit

Key takeaway: Wrap each hook with a dependency check, quotes on paths, and an || echo 'error' >&2 to capture failures.

Why does launching a subagent without sufficient context produce poor results?

Error 4 - Severity: Warning. A subagent is a Claude instance launched from Claude Code to process a subtask. Without context, it works blindly.

The subagent system (or "Task") allows parallelizing work by launching specialized agents. But each subagent starts with a blank context - it does not see your current conversation. In 2026, the average cost of a poorly configured subagent is 15,000 tokens wasted per invocation.

Incorrect:

# Overly vague subagent prompt
Fix the bugs in the project.

Correct:

# Contextualized subagent prompt
Analyze the file src/api/auth.ts and fix the TypeScript error TS2345
at line 42. The project uses:
- TypeScript 5.4 with strict mode
- Express 4.18
- The expected type is `AuthRequest` defined in src/types/auth.ts
Return only the diff of modifications.

You must provide the subagent with: the target file, the language, the involved types, and the expected output format. A well-framed subagent consumes on average 3,000 tokens versus 15,000 for one without context - an 80% cost reduction.

If you also encounter issues with an overly broad context in your main conversations, the context management errors guide details solutions for optimizing the context window.

Key takeaway: Provide the subagent with the target file, tech stack, error type, and output format in each invocation.

How to resolve name conflicts between local and global commands?

Error 5 - Severity: Warning. When a project command and a global command share the same name, the project command overrides the global one without warning.

You create a /review command in ~/.claude/commands/review.md for all your projects. Then a colleague adds .claude/commands/review.md to the repository. Your global command disappears for this project. In practice, this collision affects 1 in 5 developers in teams using commands shared via Git.

Incorrect:

# Global command
~/.claude/commands/review.md -> "Review with my personal conventions"

# Project command (same name)
.claude/commands/review.md -> "Review per team standards"

# Result: the project command overrides the global one, WITHOUT warning

Correct:

# Naming convention to avoid collisions
~/.claude/commands/my-review.md    -> Personal prefix "my-"
.claude/commands/team-review.md    -> Team prefix "team-"

# Or use subfolders (supported since Claude Code v2.0)
.claude/commands/team/review.md    -> Invoked by /team:review
~/.claude/commands/perso/review.md -> Invoked by /perso:review

Adopt a prefixing convention from day one. Subfolders create natural namespaces. See the custom commands tips to discover other proven naming conventions.

Key takeaway: Prefix your global commands with my- or perso- and your project commands with team- to eliminate name collisions.

Should the description field always be filled in a skill?

Error 6 - Severity: Warning. Omitting the description prevents Claude Code from automatically suggesting the relevant skill.

The description on the first line of the Markdown file helps Claude Code understand when to suggest your command. Without a description, the command exists but never appears in contextual suggestions. The description should fit on 1 line and be 120 characters maximum.

Incorrect:

# .claude/commands/migrate.md

Analyze the Prisma schema and generate a PostgreSQL-compatible SQL migration.
Check foreign key constraints.

Correct:

# .claude/commands/migrate.md
# Description: Generate a Prisma/PostgreSQL migration from the modified schema

Analyze the Prisma schema and generate a PostgreSQL-compatible SQL migration.
Check foreign key constraints.
Use `prisma migrate dev --name $ARGUMENTS` to apply.

Here is how to verify that your skills are properly detected: Run / in Claude Code and browse the list. Each command with a description shows a preview in the menu. Commands without a description appear with a generic label, reducing by 70% the probability that a colleague discovers them. The custom commands FAQ answers frequent questions about skill metadata.

Key takeaway: Add a # Description: comment on the first line of each command file to enable contextual suggestions.

How to prevent a hook from blocking Claude Code indefinitely?

Error 7 - Severity: Critical. A hook without a timeout can freeze your Claude Code session for several minutes.

A hook triggered on PostToolUse that calls a remote server or runs a long build blocks Claude Code as long as the process runs. Concretely, an npm run build hook on a 200,000-line monorepo can take 45 seconds - during which Claude Code is unusable.

Incorrect:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "npm run build && npm run test"
      }
    ]
  }
}

Correct:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "timeout 10 npx eslint --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
      }
    ]
  }
}
ApproachAverage timeBlocking risk
Full build in hook45sHigh
Lint only with timeout2sNone
Targeted unit test5sLow
No post-write hook0sNone

Limit hooks to fast operations - lint, formatting - and reserve builds and tests for explicit commands. If you also automate your Git workflows with hooks, the Git integration errors guide covers similar pitfalls on the pre-commit side.

Key takeaway: Wrap each hook with timeout N (in seconds) and redirect stderr to /dev/null to avoid blocking.

Why does a subagent with overly broad permissions cause problems?

Error 8 - Severity: Warning. A subagent that inherits all permissions can modify critical files or execute destructive commands.

By default, a subagent inherits the permission mode of the parent session. If you are working in bypassPermissions mode, the subagent can delete files, modify .env, or execute rm -rf without confirmation. each subagent should receive the minimum necessary permissions.

Incorrect:

# Launch a subagent in permissive mode for a read task
Use mode: "bypassPermissions" to analyze the source code

Correct:

# Launch a subagent in restricted mode suited to the task
Use subagent_type: "Explore" for search tasks (read-only)
Use subagent_type: "Plan" for planning (read-only)
Use mode: "plan" for tasks requiring approval before modification

The choice of subagent_type determines the accessible tools. An Explore agent can neither write nor edit files - it is the secure choice for code search. In practice, 25% of accidental deletion incidents come from subagents with excessive permissions. To master the complete permissions model, refer to the permissions and security errors.

Subagent typeReadWriteBashUse case
ExploreYesNoNoCode search
PlanYesNoNoPlanning
general-purposeYesYesYesImplementation
BashNoNoYesSystem scripts

Key takeaway: Choose the most restrictive subagent_type possible - Explore to read, Plan to plan, general-purpose only to modify code.

How to prevent a slash command from ignoring its arguments?

Error 9 - Severity: Minor. Forgetting the $ARGUMENTS variable in your command prompt makes user parameters inaccessible.

When you type /deploy staging, the word staging is captured in $ARGUMENTS. If your command file never references this variable, the argument is lost. The prompt executes without considering what you typed after the command name.

Incorrect:

# .claude/commands/deploy.md
Deploy the application to the appropriate environment.
Check environment variables and launch the build.

Correct:

# .claude/commands/deploy.md
# Description: Deploy the application to the specified environment

Deploy the application to the $ARGUMENTS environment.
Steps:
1. Verify that the .env.$ARGUMENTS file exists
2. Run `npm run build -- --mode $ARGUMENTS`
3. Execute `npm run deploy:$ARGUMENTS`
If $ARGUMENTS is empty, ask the user to specify: staging or production.

You can also reference $FILE to pass the currently open file. Test each command with and without arguments to validate both paths. The common errors in first conversations cover other cases where user inputs are misinterpreted.

Key takeaway: Include $ARGUMENTS in the prompt and handle the case where the variable is empty with an explicit fallback instruction.

Can you create a skill that duplicates a native Claude Code feature?

Error 10 - Severity: Minor. Recreating a native command generates behavior conflicts and complicates maintenance.

Claude Code natively includes commands like /init, /compact, /review, /memory since version 2.0 (2025). Creating a custom /compact.md skill conflicts with the native command. The resulting behavior is unpredictable - sometimes your version runs, sometimes the native one.

Incorrect:

# Create a command that shares the same name as a native command
echo "Summarize the conversation" > .claude/commands/compact.md
echo "Initialize the project" > .claude/commands/init.md

Correct:

# Extend native commands with a distinct name
echo "Summarize the conversation AND update CLAUDE.md" > .claude/commands/compact-plus.md
echo "Initialize the project with SFEIR standards" > .claude/commands/init-sfeir.md

Here is how to check existing native commands: Type / in Claude Code and scroll through the complete list. Any native command takes priority over a custom command of the same name in certain contexts. The CLAUDE.md memory guide - common errors explains how to avoid similar conflicts with the memory system.

Native commandFunctionSuggested custom alternative
/initCreates CLAUDE.md/init-custom
/compactCompresses context/compact-plus
/reviewCode review/team-review
/memoryManages memory/memory-export

Key takeaway: List native commands with / before creating a skill, and choose a distinct name to avoid conflicts.

How to structure a project with commands, skills and hooks without confusion?

Here is the architecture recommended by SFEIR Institute for organizing your Claude Code customizations. This structure separates responsibilities and facilitates team sharing.

my-project/
ā”œā”€ā”€ .claude/
│   ā”œā”€ā”€ commands/           # Project slash commands
│   │   ā”œā”€ā”€ team/           # Team namespace
│   │   │   ā”œā”€ā”€ review.md
│   │   │   └── deploy.md
│   │   └── ci/             # CI/CD namespace
│   │       └── validate.md
│   ā”œā”€ā”€ settings.json       # Hooks and permissions
│   └── CLAUDE.md           # Project memory
// .claude/settings.json - minimalist hooks
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "timeout 5 npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
      }
    ]
  },
  "permissions": {
    "allow": ["Read", "Glob", "Grep"],
    "deny": ["Bash(rm *)"]
  }
}

SFEIR Institute offers the Claude Code 1-day training to master these customizations in practice, with guided labs on creating commands, skills, and hooks. To go further, the AI-Augmented Developer training covers in 2 days the full set of automation workflows with AI agents, including subagents and task chaining.

Experienced developers can follow the AI-Augmented Developer -- Advanced 1-day training to deepen advanced customization patterns and multi-agent orchestration.

Key takeaway: Separate commands, hooks, and memory into dedicated files, and use namespaces to avoid collisions within the team.

What habits to adopt for debugging a failing command or hook?

When a slash command or hook does not work, apply this 5-step diagnostic checklist:

  1. Check the location: is the file in .claude/commands/ (project) or ~/.claude/commands/ (global)?
  2. Confirm the extension: only .md files are recognized as commands
  3. Test the hook in isolation: copy the shell command and run it manually in the terminal
  4. Inspect the logs: Claude Code displays hook errors in stderr output
  5. Validate the JSON: use cat .claude/settings.json | python3 -m json.tool to detect syntax errors
# Quick hook diagnostic
$ cat .claude/settings.json | python3 -m json.tool
# If JSON error -> fix the syntax
# If JSON valid -> test the hook command manually
$ timeout 5 npx prettier --write "test.ts" 2>&1

In practice, 55% of hook problems come from a JSON syntax error (missing comma, unclosed quote). Node.js 22 LTS is the recommended version for running CLI tools referenced in hooks.

For other diagnostic techniques, see the custom commands tips which offer automated validation scripts.

Key takeaway: Isolate the problem by testing each component separately - first the JSON, then the shell command, then the integration with Claude Code.

Recommended training

Claude Code Training

Master Claude Code with our expert instructors. Practical, hands-on training directly applicable to your projects.

View program