fix: resolve SDK spawn failures and sharp native binary crashes

- Strip CLAUDECODE env var from SDK subprocesses to prevent "cannot be
  launched inside another Claude Code session" error (Claude Code 2.1.42+)
- Lazy-load @chroma-core/default-embed to avoid eagerly pulling in
  sharp native binaries at bundle startup (fixes ERR_DLOPEN_FAILED)
- Add stderr capture to SDK spawn for diagnosing future process failures
- Exclude lockfiles from marketplace rsync and delete stale lockfiles
  before npm install to prevent native dep version mismatches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-02-13 22:47:27 -05:00
parent ed313db742
commit 1b68c55763
7 changed files with 329 additions and 312 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+11 -1
View File
@@ -61,10 +61,20 @@ function getPluginVersion() {
console.log('Syncing to marketplace...'); console.log('Syncing to marketplace...');
try { try {
execSync( execSync(
'rsync -av --delete --exclude=.git --exclude=/.mcp.json ./ ~/.claude/plugins/marketplaces/thedotmack/', 'rsync -av --delete --exclude=.git --exclude=/.mcp.json --exclude=bun.lock --exclude=package-lock.json ./ ~/.claude/plugins/marketplaces/thedotmack/',
{ stdio: 'inherit' } { stdio: 'inherit' }
); );
// Remove stale lockfiles before install — they pin old native dep versions
const { unlinkSync } = require('fs');
for (const lockfile of ['package-lock.json', 'bun.lock']) {
const lockpath = path.join(INSTALLED_PATH, lockfile);
if (existsSync(lockpath)) {
unlinkSync(lockpath);
console.log(`Removed stale ${lockfile}`);
}
}
console.log('Running npm install in marketplace...'); console.log('Running npm install in marketplace...');
execSync( execSync(
'cd ~/.claude/plugins/marketplaces/thedotmack/ && npm install', 'cd ~/.claude/plugins/marketplaces/thedotmack/ && npm install',
+3 -2
View File
@@ -13,7 +13,6 @@
*/ */
import { ChromaClient, Collection } from 'chromadb'; import { ChromaClient, Collection } from 'chromadb';
import { DefaultEmbeddingFunction } from '@chroma-core/default-embed';
import { ParsedObservation, ParsedSummary } from '../../sdk/parser.js'; import { ParsedObservation, ParsedSummary } from '../../sdk/parser.js';
import { SessionStore } from '../sqlite/SessionStore.js'; import { SessionStore } from '../sqlite/SessionStore.js';
import { logger } from '../../utils/logger.js'; import { logger } from '../../utils/logger.js';
@@ -191,7 +190,9 @@ export class ChromaSync {
try { try {
// getOrCreateCollection handles both cases // getOrCreateCollection handles both cases
// Use DefaultEmbeddingFunction for local embeddings (all-MiniLM-L6-v2) // Lazy-load DefaultEmbeddingFunction to avoid eagerly pulling in
// @huggingface/transformers → sharp native binaries at bundle startup
const { DefaultEmbeddingFunction } = await import('@chroma-core/default-embed');
const embeddingFunction = new DefaultEmbeddingFunction(); const embeddingFunction = new DefaultEmbeddingFunction();
this.collection = await this.chromaClient.getOrCreateCollection({ this.collection = await this.chromaClient.getOrCreateCollection({
name: this.collectionName, name: this.collectionName,
+12 -1
View File
@@ -290,12 +290,22 @@ export function createPidCapturingSpawn(sessionDbId: number) {
windowsHide: true windowsHide: true
}); });
// Capture stderr for debugging spawn failures
if (child.stderr) {
child.stderr.on('data', (data: Buffer) => {
logger.debug('SDK_SPAWN', `[session-${sessionDbId}] stderr: ${data.toString().trim()}`);
});
}
// Register PID // Register PID
if (child.pid) { if (child.pid) {
registerProcess(child.pid, sessionDbId, child); registerProcess(child.pid, sessionDbId, child);
// Auto-unregister on exit // Auto-unregister on exit
child.on('exit', () => { child.on('exit', (code: number | null, signal: string | null) => {
if (code !== 0) {
logger.warn('SDK_SPAWN', `[session-${sessionDbId}] Claude process exited`, { code, signal, pid: child.pid });
}
if (child.pid) { if (child.pid) {
unregisterProcess(child.pid); unregisterProcess(child.pid);
} }
@@ -306,6 +316,7 @@ export function createPidCapturingSpawn(sessionDbId: number) {
return { return {
stdin: child.stdin, stdin: child.stdin,
stdout: child.stdout, stdout: child.stdout,
stderr: child.stderr,
get killed() { return child.killed; }, get killed() { return child.killed; },
get exitCode() { return child.exitCode; }, get exitCode() { return child.exitCode; },
kill: child.kill.bind(child), kill: child.kill.bind(child),
+1
View File
@@ -27,6 +27,7 @@ export const ENV_FILE_PATH = join(DATA_DIR, '.env');
// are passed through to avoid breaking CLI authentication, proxies, and platform features. // are passed through to avoid breaking CLI authentication, proxies, and platform features.
const BLOCKED_ENV_VARS = [ const BLOCKED_ENV_VARS = [
'ANTHROPIC_API_KEY', // Issue #733: Prevent auto-discovery from project .env files 'ANTHROPIC_API_KEY', // Issue #733: Prevent auto-discovery from project .env files
'CLAUDECODE', // Prevent "cannot be launched inside another Claude Code session" error
]; ];
// Credential keys that claude-mem manages // Credential keys that claude-mem manages