{"record":{"id":"8644594b1af14e42","repo":"thedotmack/claude-mem","slug":"failed-to-parse-transcript-line-non-error-thrown","errorCode":null,"errorMessage":"Failed to parse transcript line (non-Error thrown)","messagePattern":"Failed to parse transcript line \\(non-Error thrown\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/transcripts/watcher.ts","lineNumber":294,"sourceCode":"\n  private async handleLine(\n    line: string,\n    watch: WatchTarget,\n    schema: TranscriptSchema,\n    filePath: string,\n    sessionIdOverride?: string | null\n  ): Promise<void> {\n    try {\n      const entry = JSON.parse(line);\n      await this.processor.processEntry(entry, watch, schema, sessionIdOverride ?? undefined);\n    } catch (error: unknown) {\n      if (error instanceof Error) {\n        logger.debug('TRANSCRIPT', 'Failed to parse transcript line', {\n          watch: watch.name,\n          file: basename(filePath)\n        }, error);\n      } else {\n        logger.warn('TRANSCRIPT', 'Failed to parse transcript line (non-Error thrown)', {\n          watch: watch.name,\n          file: basename(filePath),\n          error: String(error)\n        });\n      }\n    }\n  }\n\n  private extractSessionIdFromPath(filePath: string): string | null {\n    const match = filePath.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);\n    return match ? match[0] : null;\n  }\n}\n","sourceCodeStart":276,"sourceCodeEnd":308,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/transcripts/watcher.ts#L276-L308","documentation":"Each line tailed from a transcript .jsonl file goes through JSON.parse and processor.processEntry inside one try block. The catch distinguishes real Error instances (logged at debug) from non-Error thrown values; this warning fires when something in that block threw a string, number, or plain object instead of an Error. JSON.parse itself always throws SyntaxError, so a non-Error means processEntry (or code it calls) threw a bare value.","triggerScenarios":"A custom or plugin-supplied processEntry path executing `throw 'invalid entry'`; a Promise rejection with a non-Error payload inside processEntry; exotic objects (e.g. from vm realms) failing `instanceof Error`.","commonSituations":"Tail happens to read a half-written trailing line while a session is active (usually surfaces as the debug SyntaxError path, but non-Error throws hide the real cause); third-party transcript schema processors throwing strings; DOM-exception-like objects from polyfilled environments.","solutions":["Check the logged `error` field — String(error) shows the bare value that was thrown, which identifies the throwing module","Make custom processors throw `new Error(...)` (or Error subclasses) so they land in the debug path with stack traces","If the line is a partial trailing write, no action is needed — the next append re-delivers the complete line"],"exampleFix":"// before (in a custom processor)\nif (!entry.type) throw 'invalid entry';\n\n// after\nif (!entry.type) throw new Error(`invalid transcript entry: missing type in ${filePath}`);","handlingStrategy":"try-catch","validationCode":"function isCompleteJsonLine(line: string): boolean {\n  const t = line.trim();\n  if (!t) return false;\n  try { JSON.parse(t); return true; } catch { return false; }\n}","typeGuard":"function isErrorLike(value: unknown): value is Error {\n  return value instanceof Error || (typeof value === 'object' && value !== null && 'name' in value && 'message' in value);","tryCatchPattern":"try {\n  const entry = JSON.parse(line);\n  await processor.processEntry(entry, watch, schema);\n} catch (error: unknown) {\n  const normalized = error instanceof Error ? error : new Error(String(error));\n  logger.warn('TRANSCRIPT', 'Failed to parse transcript line', { watch: watch.name, file: basename(filePath) }, normalized);\n}","preventionTips":["Always throw Error instances from processors so stack traces survive the catch","Skip empty and visibly truncated trailing lines before parsing","Log the raw non-Error value (String(error)) — it is the only clue to the throwing module"],"tags":["jsonl","transcript-watcher","error-handling"],"backgroundTag":"json-parse-error","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}