Files
claude-mem/docs/reports/issue-599-windows-drive-root-400-error.md
Alex Newman 2659ec3231 fix: Claude Code 2.1.1 compatibility + log-level audit + path validation fixes (#614)
* Refactor CLAUDE.md and related files for December 2025 updates

- Updated CLAUDE.md in src/services/worker with new entries for December 2025, including changes to Search.ts, GeminiAgent.ts, SDKAgent.ts, and SessionManager.ts.
- Revised CLAUDE.md in src/shared to reflect updates and new entries for December 2025, including paths.ts and worker-utils.ts.
- Modified hook-constants.ts to clarify exit codes and their behaviors.
- Added comprehensive hooks reference documentation for Claude Code, detailing usage, events, and examples.
- Created initial CLAUDE.md files in various directories to track recent activity.

* fix: Merge user-message-hook output into context-hook hookSpecificOutput

- Add footer message to additionalContext in context-hook.ts
- Remove user-message-hook from SessionStart hooks array
- Fixes issue where stderr+exit(1) approach was silently discarded

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update logs and documentation for recent plugin and worker service changes

- Added detailed logs for worker service activities from Dec 10, 2025 to Jan 7, 2026, including initialization patterns, cleanup confirmations, and diagnostic logging.
- Updated plugin documentation with recent activities, including plugin synchronization and configuration changes from Dec 3, 2025 to Jan 7, 2026.
- Enhanced the context hook and worker service logs to reflect improvements and fixes in the plugin architecture.
- Documented the migration and verification processes for the Claude memory system and its integration with the marketplace.

* Refactor hooks architecture and remove deprecated user-message-hook

- Updated hook configurations in CLAUDE.md and hooks.json to reflect changes in session start behavior.
- Removed user-message-hook functionality as it is no longer utilized in Claude Code 2.1.0; context is now injected silently.
- Enhanced context-hook to handle session context injection without user-visible messages.
- Cleaned up documentation across multiple files to align with the new hook structure and removed references to obsolete hooks.
- Adjusted timing and command execution for hooks to improve performance and reliability.

* fix: Address PR #610 review issues

- Replace USER_MESSAGE_ONLY test with BLOCKING_ERROR test in hook-constants.test.ts
- Standardize Claude Code 2.1.0 note wording across all three documentation files
- Exclude deprecated user-message-hook.ts from logger-usage-standards test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Remove hardcoded fake token counts from context injection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR #610 review issues by fixing test files, standardizing documentation notes, and verifying code quality improvements.

* fix: Add path validation to CLAUDE.md distribution to prevent invalid directory creation

- Add isValidPathForClaudeMd() function to reject invalid paths:
  - Tilde paths (~) that Node.js doesn't expand
  - URLs (http://, https://)
  - Paths with spaces (likely command text or PR references)
  - Paths with # (GitHub issue/PR references)
  - Relative paths that escape project boundary

- Integrate validation in updateFolderClaudeMdFiles loop
- Add 6 unit tests for path validation
- Update .gitignore to prevent accidental commit of malformed directories
- Clean up existing invalid directories (~/, PR #610..., git diff..., https:)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Implement path validation in CLAUDE.md generation to prevent invalid directory creation

- Added `isValidPathForClaudeMd()` function to validate file paths in `src/utils/claude-md-utils.ts`.
- Integrated path validation in `updateFolderClaudeMdFiles` to skip invalid paths.
- Added 6 new unit tests in `tests/utils/claude-md-utils.test.ts` to cover various rejection cases.
- Updated `.gitignore` to prevent tracking of invalid directories.
- Cleaned up existing invalid directories in the repository.

* feat: Promote critical WARN logs to ERROR level across codebase

Comprehensive log-level audit promoting 38+ WARN messages to ERROR for
improved debugging and incident response:

- Parser: observation type errors, data contamination
- SDK/Agents: empty init responses (Gemini, OpenRouter)
- Worker/Queue: session recovery, auto-recovery failures
- Chroma: sync failures, search failures (now treated as critical)
- SQLite: search failures (primary data store)
- Session/Generator: failures, missing context
- Infrastructure: shutdown, process management failures
- File Operations: CLAUDE.md updates, config reads
- Branch Management: recovery checkout failures

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Address PR #614 review issues

- Remove incorrectly tracked tilde-prefixed files from git
- Fix absolute path validation to check projectRoot boundaries
- Add test coverage for absolute path validation edge cases

Closes review issues:
- Issue 1: ~/ prefixed files removed from tracking
- Issue 3: Absolute paths now validated against projectRoot
- Issue 4: Added 3 new test cases for absolute path scenarios

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* build assets and context

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:34:20 -05:00

10 KiB

Technical Report: Issue #599 - Windows Drive Root 400 Error

Issue: #599 Title: user-message-hook.js fails with 400 error when running from Windows drive root (C:) Author: PakAbhishek Created: 2026-01-07 Severity: Low Priority: Medium Component: Hooks / Session Initialization


1. Executive Summary

When running Claude Code from a Windows drive root directory (e.g., C:\), the user-message-hook.js script fails with a 400 HTTP error during session startup. The root cause is that path.basename('C:\') returns an empty string on Windows, which causes the API call to /api/context/inject?project= to fail with the error "Project(s) parameter is required".

Key Findings:

  • The bug is cosmetic only - all core memory functionality continues to work correctly
  • A robust fix already exists in src/utils/project-name.ts (getProjectName() function) but is not used by user-message-hook.ts
  • The fix requires updating user-message-hook.ts to use the existing getProjectName() utility instead of raw path.basename()
  • The context-hook.ts is already immune to this bug because it uses getProjectContext() which wraps getProjectName()

Affected Files:

  • src/hooks/user-message-hook.ts (needs fix)
  • plugin/scripts/user-message-hook.js (built artifact, auto-fixed by rebuild)

2. Problem Analysis

2.1 User-Reported Symptoms

  1. Error message on Claude Code startup when cwd is C:\:

    error: Failed to fetch context: 400
          at C:\Users\achau\.claude\plugins\cache\thedotmack\claude-mem\9.0.0\scripts\user-message-hook.js:19:1339
    
  2. The error appears during the SessionStart hook phase

  3. Despite the error, all other functionality works correctly:

    • Worker health check: passing
    • MCP tools: connected and functional
    • Memory search: working
    • Session observations: saved correctly

2.2 Reproduction Steps

  1. Open terminal on Windows
  2. Navigate to drive root: cd C:\
  3. Start Claude Code: claude
  4. Observe the startup error

2.3 Environment

  • OS: Windows 11
  • claude-mem version: 9.0.0
  • Bun version: 1.3.5
  • Claude Code: Latest

3. Technical Details

3.1 Code Flow Analysis

The user-message-hook.ts extracts the project name using:

// File: src/hooks/user-message-hook.ts (lines 18-23)
const project = basename(process.cwd());

const response = await fetch(
  `http://127.0.0.1:${port}/api/context/inject?project=${encodeURIComponent(project)}&colors=true`,
  { method: 'GET' }
);

When process.cwd() returns C:\, the path.basename() function returns an empty string:

> require('path').basename('C:\\')
''

This results in an API call to:

/api/context/inject?project=&colors=true

3.2 Server-Side Validation

The /api/context/inject endpoint in SearchRoutes.ts performs strict validation:

// File: src/services/worker/http/routes/SearchRoutes.ts (lines 207-223)
private handleContextInject = this.wrapHandler(async (req: Request, res: Response): Promise<void> => {
  const projectsParam = (req.query.projects as string) || (req.query.project as string);
  const useColors = req.query.colors === 'true';

  if (!projectsParam) {
    this.badRequest(res, 'Project(s) parameter is required');
    return;
  }

  const projects = projectsParam.split(',').map(p => p.trim()).filter(Boolean);

  if (projects.length === 0) {
    this.badRequest(res, 'At least one project is required');
    return;
  }
  // ...
});

The validation correctly rejects empty project names, returning HTTP 400.

3.3 Existing Solution

A robust solution already exists in src/utils/project-name.ts:

// File: src/utils/project-name.ts (lines 12-40)
export function getProjectName(cwd: string | null | undefined): string {
  if (!cwd || cwd.trim() === '') {
    logger.warn('PROJECT_NAME', 'Empty cwd provided, using fallback', { cwd });
    return 'unknown-project';
  }

  const basename = path.basename(cwd);

  // Edge case: Drive roots on Windows (C:\, J:\) or Unix root (/)
  // path.basename('C:\') returns '' (empty string)
  if (basename === '') {
    const isWindows = process.platform === 'win32';
    if (isWindows) {
      const driveMatch = cwd.match(/^([A-Z]):\\/i);
      if (driveMatch) {
        const driveLetter = driveMatch[1].toUpperCase();
        const projectName = `drive-${driveLetter}`;
        logger.info('PROJECT_NAME', 'Drive root detected', { cwd, projectName });
        return projectName;
      }
    }
    logger.warn('PROJECT_NAME', 'Root directory detected, using fallback', { cwd });
    return 'unknown-project';
  }

  return basename;
}

This function:

  • Handles null/undefined cwd
  • Handles empty basename (drive roots)
  • Returns meaningful names like drive-C, drive-D for Windows drive roots
  • Returns unknown-project for Unix root or other edge cases

3.4 Comparison: Fixed vs. Unfixed Hooks

Hook Implementation Status
context-hook.ts Uses getProjectContext() which calls getProjectName() Immune to bug
user-message-hook.ts Uses raw basename(process.cwd()) Vulnerable
new-hook.ts Receives cwd from stdin, uses getProjectName() Immune to bug
save-hook.ts Uses basename but receives cwd from API context Context-dependent

4. Impact Assessment

4.1 Severity: Low

  • Functional Impact: Cosmetic only - the error message is displayed but does not affect core functionality
  • Data Integrity: No data loss or corruption
  • Workaround Available: Yes - run Claude from a project directory instead of drive root

4.2 Affected Users

  • Users running Claude Code from Windows drive roots (C:, D:, etc.)
  • Estimated as a small percentage of users based on typical usage patterns
  • More likely to affect users doing quick tests or troubleshooting

4.3 User Experience Impact

  • Confusing error message on startup
  • Users may incorrectly believe the plugin is broken
  • Error appears in stderr alongside legitimate context information

5. Root Cause Analysis

5.1 Primary Cause

The user-message-hook.ts was implemented using a direct path.basename() call instead of the standardized getProjectName() utility function that handles edge cases.

5.2 Contributing Factors

  1. Inconsistent Pattern Usage: Different hooks use different approaches to extract project names
  2. Missing Validation: No client-side validation of project name before making API call
  3. Edge Case Not Tested: Windows drive root is an unusual but valid working directory

5.3 Historical Context

The getProjectName() utility was added to handle this exact edge case (see src/utils/project-name.ts), but not all hooks were updated to use it. The context-hook.ts uses the newer getProjectContext() function, while user-message-hook.ts still uses the older pattern.


Update user-message-hook.ts to use the existing getProjectName() utility:

// Current (vulnerable):
import { basename } from "path";
const project = basename(process.cwd());

// Fixed:
import { getProjectName } from "../utils/project-name.js";
const project = getProjectName(process.cwd());

Benefits:

  • Uses battle-tested utility
  • Consistent with other hooks
  • Handles all edge cases (drive roots, Unix root, empty cwd)
  • Provides meaningful project names (drive-C) instead of fallbacks

6.2 Alternative: Inline Fix (User-Suggested)

The user suggested an inline fix in the issue:

let projectName = basename(process.cwd());
if (!projectName || projectName === '') {
  const cwd = process.cwd();
  projectName = cwd.match(/^([A-Za-z]:)[\\/]?$/)
    ? `drive-${cwd[0].toUpperCase()}`
    : 'unknown-project';
}

Evaluation:

  • Functionally correct
  • Duplicates existing logic in getProjectName()
  • Does not address the pattern inconsistency
  • Acceptable if import constraints prevent using the utility

6.3 Additional Improvements (Optional)

  1. Add Client-Side Validation:

    if (!project || project.trim() === '') {
      throw new Error('Unable to determine project name from working directory');
    }
    
  2. Standardize All Hooks: Audit other hooks using basename(process.cwd()) and update to use getProjectName()

  3. Add Unit Tests: Create tests for user-message-hook.ts covering:

    • Normal project directories
    • Windows drive roots (C:, D:)
    • Unix root (/)
    • Trailing slashes

7. Priority and Severity Assessment

7.1 Classification

Metric Value Justification
Severity Low Cosmetic error only, no functional impact
Priority Medium User-facing error, easy fix, affects Windows users
Effort Trivial Single line change + rebuild
Risk Very Low Using existing, tested utility function

7.2 Recommendation

Recommended Action: Fix in next patch release (9.0.1)

Rationale:

  • Simple fix with minimal risk
  • Improves Windows user experience
  • Demonstrates responsiveness to community feedback
  • Pattern already exists in codebase

7.3 Testing Requirements

  1. Verify fix on Windows with C:\ as cwd
  2. Verify existing behavior unchanged for normal project directories
  3. Verify worktree detection still works correctly
  4. Run full hook test suite

8. Appendix

File Purpose Fix Required
/Users/alexnewman/conductor/workspaces/claude-mem/budapest/src/hooks/user-message-hook.ts Source hook (needs fix) Yes
/Users/alexnewman/conductor/workspaces/claude-mem/budapest/plugin/scripts/user-message-hook.js Built hook Auto-rebuilds
/Users/alexnewman/conductor/workspaces/claude-mem/budapest/src/utils/project-name.ts Utility (has fix) No
/Users/alexnewman/conductor/workspaces/claude-mem/budapest/src/hooks/context-hook.ts Reference implementation No
/Users/alexnewman/conductor/workspaces/claude-mem/budapest/src/services/worker/http/routes/SearchRoutes.ts API validation No
  • Windows compatibility has been a focus area, with 56+ memory entries documenting Windows-specific fixes
  • This issue follows the pattern of other Windows edge case bugs

8.3 References