Standardize and enhance error handling across hooks and worker service (#295)
* Enhance error logging in hooks
- Added detailed error logging in context-hook, new-hook, save-hook, and summary-hook to capture status, project, port, and relevant session information on failures.
- Improved error messages thrown in save-hook and summary-hook to include specific context about the failure.
* Refactor migration logging to use console.log instead of console.error
- Updated SessionSearch and SessionStore classes to replace console.error with console.log for migration-related messages.
- Added notes in the documentation to clarify the use of console.log for migration messages due to the unavailability of the structured logger during constructor execution.
* Refactor SDKAgent and silent-debug utility to simplify error handling
- Updated SDKAgent to use direct defaults instead of happy_path_error__with_fallback for non-critical fields such as last_user_message, last_assistant_message, title, filesRead, filesModified, concepts, and summary.request.
- Enhanced silent-debug documentation to clarify appropriate use cases for happy_path_error__with_fallback, emphasizing its role in handling unexpected null/undefined values while discouraging its use for nullable fields with valid defaults.
* fix: correct happy_path_error__with_fallback usage to prevent false errors
Fixes false "Missing cwd" and "Missing transcript_path" errors that were
flooding silent.log even when values were present.
Root cause: happy_path_error__with_fallback was being called unconditionally
instead of only when the value was actually missing.
Pattern changed from:
value: happy_path_error__with_fallback('Missing', {}, value || '')
To correct usage:
value: value || happy_path_error__with_fallback('Missing', {}, '')
Fixed in:
- src/hooks/save-hook.ts (PostToolUse hook)
- src/hooks/summary-hook.ts (Stop hook)
- src/services/worker/http/routes/SessionRoutes.ts (2 instances)
Impact: Eliminates false error noise, making actual errors visible.
Addresses issue #260 - users were seeing "Missing cwd" errors despite
Claude Code correctly passing all required fields.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Enhance error logging and handling across services
- Improved error messages in SessionStore to include project context when fetching boundary observations and timestamps.
- Updated ChromaSync error handling to provide more informative messages regarding client initialization failures, including the project context.
- Enhanced error logging in WorkerService to include the package path when reading version fails.
- Added detailed error logging in worker-utils to capture expected and running versions during health checks.
- Extended WorkerErrorMessageOptions to include actualError for more informative restart instructions.
* Refactor error handling in hooks to use standardized fetch error handler
- Introduced a new error handler `handleFetchError` in `shared/error-handler.ts` to standardize logging and user-facing error messages for fetch failures across hooks.
- Updated `context-hook.ts`, `new-hook.ts`, `save-hook.ts`, and `summary-hook.ts` to utilize the new error handler, improving consistency and maintainability.
- Removed redundant imports and error handling logic related to worker restart instructions from the hooks.
* feat: add comprehensive error handling tests for hooks and ChromaSync client
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,9 @@ export class SessionSearch {
|
||||
* - Tables maintained but search paths removed
|
||||
* - Triggers still fire to keep tables synchronized
|
||||
*
|
||||
* Note: Using console.log for migration messages since they run during constructor
|
||||
* before structured logger is available. Actual errors use console.error.
|
||||
*
|
||||
* TODO: Remove FTS5 infrastructure in future major version (v7.0.0)
|
||||
*/
|
||||
private ensureFTSTables(): void {
|
||||
@@ -57,7 +60,7 @@ export class SessionSearch {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('[SessionSearch] Creating FTS5 tables...');
|
||||
console.log('[SessionSearch] Creating FTS5 tables...');
|
||||
|
||||
// Create observations_fts virtual table
|
||||
this.db.run(`
|
||||
@@ -141,7 +144,7 @@ export class SessionSearch {
|
||||
END;
|
||||
`);
|
||||
|
||||
console.error('[SessionSearch] FTS5 tables created successfully');
|
||||
console.log('[SessionSearch] FTS5 tables created successfully');
|
||||
} catch (error: any) {
|
||||
console.error('[SessionSearch] FTS migration error:', error.message);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ export class SessionStore {
|
||||
/**
|
||||
* Initialize database schema using migrations (migration004)
|
||||
* This runs the core SDK tables migration if no tables exist
|
||||
*
|
||||
* Note: Using console.log for migration messages since they run during constructor
|
||||
* before structured logger is available. Actual errors use console.error.
|
||||
*/
|
||||
private initializeSchema(): void {
|
||||
try {
|
||||
@@ -64,7 +67,7 @@ export class SessionStore {
|
||||
// Only run migration004 if no migrations have been applied
|
||||
// This creates the sdk_sessions, observations, and session_summaries tables
|
||||
if (maxApplied === 0) {
|
||||
console.error('[SessionStore] Initializing fresh database with migration004...');
|
||||
console.log('[SessionStore] Initializing fresh database with migration004...');
|
||||
|
||||
// Migration004: SDK agent architecture tables
|
||||
this.db.run(`
|
||||
@@ -128,7 +131,7 @@ export class SessionStore {
|
||||
// Record migration004 as applied
|
||||
this.db.prepare('INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)').run(4, new Date().toISOString());
|
||||
|
||||
console.error('[SessionStore] Migration004 applied successfully');
|
||||
console.log('[SessionStore] Migration004 applied successfully');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('[SessionStore] Schema initialization error:', error.message);
|
||||
@@ -151,7 +154,7 @@ export class SessionStore {
|
||||
|
||||
if (!hasWorkerPort) {
|
||||
this.db.run('ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER');
|
||||
console.error('[SessionStore] Added worker_port column to sdk_sessions table');
|
||||
console.log('[SessionStore] Added worker_port column to sdk_sessions table');
|
||||
}
|
||||
|
||||
// Record migration
|
||||
@@ -176,7 +179,7 @@ export class SessionStore {
|
||||
|
||||
if (!hasPromptCounter) {
|
||||
this.db.run('ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0');
|
||||
console.error('[SessionStore] Added prompt_counter column to sdk_sessions table');
|
||||
console.log('[SessionStore] Added prompt_counter column to sdk_sessions table');
|
||||
}
|
||||
|
||||
// Check observations for prompt_number
|
||||
@@ -185,7 +188,7 @@ export class SessionStore {
|
||||
|
||||
if (!obsHasPromptNumber) {
|
||||
this.db.run('ALTER TABLE observations ADD COLUMN prompt_number INTEGER');
|
||||
console.error('[SessionStore] Added prompt_number column to observations table');
|
||||
console.log('[SessionStore] Added prompt_number column to observations table');
|
||||
}
|
||||
|
||||
// Check session_summaries for prompt_number
|
||||
@@ -194,7 +197,7 @@ export class SessionStore {
|
||||
|
||||
if (!sumHasPromptNumber) {
|
||||
this.db.run('ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER');
|
||||
console.error('[SessionStore] Added prompt_number column to session_summaries table');
|
||||
console.log('[SessionStore] Added prompt_number column to session_summaries table');
|
||||
}
|
||||
|
||||
// Record migration
|
||||
@@ -223,7 +226,7 @@ export class SessionStore {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id...');
|
||||
console.log('[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id...');
|
||||
|
||||
// Begin transaction
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
@@ -278,7 +281,7 @@ export class SessionStore {
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(7, new Date().toISOString());
|
||||
|
||||
console.error('[SessionStore] Successfully removed UNIQUE constraint from session_summaries.sdk_session_id');
|
||||
console.log('[SessionStore] Successfully removed UNIQUE constraint from session_summaries.sdk_session_id');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.run('ROLLBACK');
|
||||
@@ -308,7 +311,7 @@ export class SessionStore {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('[SessionStore] Adding hierarchical fields to observations table...');
|
||||
console.log('[SessionStore] Adding hierarchical fields to observations table...');
|
||||
|
||||
// Add new columns
|
||||
this.db.run(`
|
||||
@@ -324,7 +327,7 @@ export class SessionStore {
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(8, new Date().toISOString());
|
||||
|
||||
console.error('[SessionStore] Successfully added hierarchical fields to observations table');
|
||||
console.log('[SessionStore] Successfully added hierarchical fields to observations table');
|
||||
} catch (error: any) {
|
||||
console.error('[SessionStore] Migration error (add hierarchical fields):', error.message);
|
||||
}
|
||||
@@ -350,7 +353,7 @@ export class SessionStore {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('[SessionStore] Making observations.text nullable...');
|
||||
console.log('[SessionStore] Making observations.text nullable...');
|
||||
|
||||
// Begin transaction
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
@@ -407,7 +410,7 @@ export class SessionStore {
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(9, new Date().toISOString());
|
||||
|
||||
console.error('[SessionStore] Successfully made observations.text nullable');
|
||||
console.log('[SessionStore] Successfully made observations.text nullable');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.run('ROLLBACK');
|
||||
@@ -435,7 +438,7 @@ export class SessionStore {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('[SessionStore] Creating user_prompts table with FTS5 support...');
|
||||
console.log('[SessionStore] Creating user_prompts table with FTS5 support...');
|
||||
|
||||
// Begin transaction
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
@@ -494,7 +497,7 @@ export class SessionStore {
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(10, new Date().toISOString());
|
||||
|
||||
console.error('[SessionStore] Successfully created user_prompts table with FTS5 support');
|
||||
console.log('[SessionStore] Successfully created user_prompts table with FTS5 support');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.run('ROLLBACK');
|
||||
@@ -522,7 +525,7 @@ export class SessionStore {
|
||||
|
||||
if (!obsHasDiscoveryTokens) {
|
||||
this.db.run('ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0');
|
||||
console.error('[SessionStore] Added discovery_tokens column to observations table');
|
||||
console.log('[SessionStore] Added discovery_tokens column to observations table');
|
||||
}
|
||||
|
||||
// Check if discovery_tokens column exists in session_summaries table
|
||||
@@ -531,7 +534,7 @@ export class SessionStore {
|
||||
|
||||
if (!sumHasDiscoveryTokens) {
|
||||
this.db.run('ALTER TABLE session_summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0');
|
||||
console.error('[SessionStore] Added discovery_tokens column to session_summaries table');
|
||||
console.log('[SessionStore] Added discovery_tokens column to session_summaries table');
|
||||
}
|
||||
|
||||
// Record migration only after successful column verification/addition
|
||||
@@ -1251,7 +1254,7 @@ export class SessionStore {
|
||||
now.toISOString(),
|
||||
nowEpoch
|
||||
);
|
||||
console.error(`[SessionStore] Auto-created session record for session_id: ${sdkSessionId}`);
|
||||
console.log(`[SessionStore] Auto-created session record for session_id: ${sdkSessionId}`);
|
||||
}
|
||||
|
||||
const stmt = this.db.prepare(`
|
||||
@@ -1325,7 +1328,7 @@ export class SessionStore {
|
||||
now.toISOString(),
|
||||
nowEpoch
|
||||
);
|
||||
console.error(`[SessionStore] Auto-created session record for session_id: ${sdkSessionId}`);
|
||||
console.log(`[SessionStore] Auto-created session record for session_id: ${sdkSessionId}`);
|
||||
}
|
||||
|
||||
const stmt = this.db.prepare(`
|
||||
@@ -1531,7 +1534,7 @@ export class SessionStore {
|
||||
startEpoch = beforeRecords.length > 0 ? beforeRecords[beforeRecords.length - 1].created_at_epoch : anchorEpoch;
|
||||
endEpoch = afterRecords.length > 0 ? afterRecords[afterRecords.length - 1].created_at_epoch : anchorEpoch;
|
||||
} catch (err: any) {
|
||||
console.error('[SessionStore] Error getting boundary observations:', err.message);
|
||||
console.error('[SessionStore] Error getting boundary observations:', err.message, project ? `(project: ${project})` : '(all projects)');
|
||||
return { observations: [], sessions: [], prompts: [] };
|
||||
}
|
||||
} else {
|
||||
@@ -1563,7 +1566,7 @@ export class SessionStore {
|
||||
startEpoch = beforeRecords.length > 0 ? beforeRecords[beforeRecords.length - 1].created_at_epoch : anchorEpoch;
|
||||
endEpoch = afterRecords.length > 0 ? afterRecords[afterRecords.length - 1].created_at_epoch : anchorEpoch;
|
||||
} catch (err: any) {
|
||||
console.error('[SessionStore] Error getting boundary timestamps:', err.message);
|
||||
console.error('[SessionStore] Error getting boundary timestamps:', err.message, project ? `(project: ${project})` : '(all projects)');
|
||||
return { observations: [], sessions: [], prompts: [] };
|
||||
}
|
||||
}
|
||||
@@ -1618,7 +1621,7 @@ export class SessionStore {
|
||||
}))
|
||||
};
|
||||
} catch (err: any) {
|
||||
console.error('[SessionStore] Error querying timeline records:', err.message);
|
||||
console.error('[SessionStore] Error querying timeline records:', err.message, project ? `(project: ${project})` : '(all projects)');
|
||||
return { observations: [], sessions: [], prompts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user