mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-25 12:00:02 +08:00
fix(db): a database SQLite can't open is reported as that, not as a Node version problem (#2796)
Root cause: createDatabase() caught everything the node:sqlite connection threw and reported it as "CodeGraph requires node:sqlite (Node.js 22.5+)", though the module had loaded and SQLite itself had failed: a database another program held open (SQLITE_CANTOPEN on Windows), a folder the user can't write, an I/O error on a network share. Failures in the connection's first statements (locked, not a database, a WAL I/O error) came out raw, without the path. The MCP server then told the agent "No CodeGraph project is loaded" for a default project whose index exists, or "Tool execution failed: ... requires node:sqlite" for a projectPath one. Meanwhile a runtime that really lacks node:sqlite never got the hint: isInitialized's schema probe loaded the module outside its try, so the CLI and MCP server died with "Unhandled rejection: No such built-in module: node:sqlite". Fix: - src/db/node-sqlite.ts loads node:sqlite in one place; NodeSqliteUnavailableError carries the Node 22.5+ hint, now only for a module that cannot be loaded. The schema probe treats it as "unknown", so the command goes on to the open and reports it from inside its own catch. - src/db/open-error.ts: DatabaseOpenError names SQLite's code and the path, with a hint for BUSY, PERM, READONLY, IOERR (network share), CORRUPT, FULL, CANTOPEN (folder, permissions, another program holding the file) and NOTADB, keeping code/errcode/errstr and the cause. The Windows/WSL shared-index guidance still wins for its case. - DatabaseConnection.open/initialize wrap the connection's first statements as well as the constructor, and close the connection on failure. - MCP: the default project's open failure is reported to a tool call instead of "no project loaded", and a DatabaseOpenError or a missing node:sqlite is answered isError with the retry-once note (a malfunction, per AGENTS.md), worded as what failed rather than "an internal codegraph error". Not-indexed projects keep their success-shaped guidance. Validation: - Windows, an indexed project whose codegraph.db another process holds open with no sharing (a real SQLITE_CANTOPEN): CLI status, sync and query, an MCP default-project call and a projectPath call all said "requires node:sqlite" on main, or "No CodeGraph project is loaded" for the default project; with the fix all name SQLITE_CANTOPEN, the path and the hint. With --no-experimental-sqlite (node:sqlite unloadable), main's CLI and MCP server died with an unhandled rejection; the fix prints the Node 22.5+ explanation and the MCP call answers with it. - New __tests__/sqlite-open-error.test.ts (21 cases): main's source 18 fail / 3 pass, fix 21 pass. Real CANTOPEN (a directory, a file held exclusively), NOTADB and BUSY opens, the CLI and the MCP engine, plus each SQLite code's message. 5 mutations each failed at least one case. - The POSIX-only wsl-shared-index case for a real I/O error outside WSL now expects the named error (DatabaseOpenError, SQLITE_IOERR, the path) instead of the bare "disk I/O error"; it is skipped on Windows, so it was not run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1040d09f26
commit
63ca5681db
+32
-1
@@ -10,8 +10,11 @@ import * as path from 'path';
|
||||
import { SchemaVersion } from '../types';
|
||||
import { runMigrations, getCurrentVersion, CURRENT_SCHEMA_VERSION } from './migrations';
|
||||
import { getCodeGraphDir, statInode } from '../directory';
|
||||
import { toDatabaseOpenError } from './open-error';
|
||||
|
||||
export { SqliteDatabase, SqliteBackend } from './sqlite-adapter';
|
||||
export { DatabaseOpenError } from './open-error';
|
||||
export { NodeSqliteUnavailableError } from './node-sqlite';
|
||||
|
||||
/**
|
||||
* Apply connection-level PRAGMAs. Shared by `initialize` and `open` so the two
|
||||
@@ -44,6 +47,22 @@ function configureConnection(db: SqliteDatabase, readOnly = false): void {
|
||||
db.pragma(`journal_size_limit = ${WAL_HEAL_THRESHOLD_BYTES}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the rest of an open on the connection `db`. Its first statements read
|
||||
* the file, so a database SQLite cannot use (locked, damaged, not a database,
|
||||
* an I/O error) fails here as well as when the connection is made. Such a
|
||||
* failure names SQLite's code and the path ({@link DatabaseOpenError}), and
|
||||
* leaves no connection open.
|
||||
*/
|
||||
function withOpenFailures<T>(db: SqliteDatabase, dbPath: string, finish: () => T): T {
|
||||
try {
|
||||
return finish();
|
||||
} catch (err) {
|
||||
try { db.close(); } catch { /* the open's failure is the one to report */ }
|
||||
throw toDatabaseOpenError(err, dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WAL size past which `healOversizedWal` (run at every `open`) checkpoints and
|
||||
* truncates the file, and to which `journal_size_limit` clips the WAL after any
|
||||
@@ -107,9 +126,12 @@ export class DatabaseConnection {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create and configure database
|
||||
const { db, backend } = createDatabase(dbPath);
|
||||
return withOpenFailures(db, dbPath, () => DatabaseConnection.initializeOn(db, backend, dbPath));
|
||||
}
|
||||
|
||||
/** The rest of {@link initialize}: configure the connection and create the schema. */
|
||||
private static initializeOn(db: SqliteDatabase, backend: SqliteBackend, dbPath: string): DatabaseConnection {
|
||||
configureConnection(db);
|
||||
|
||||
// Run schema initialization, splitting FTS5 from the rest so
|
||||
@@ -175,7 +197,16 @@ export class DatabaseConnection {
|
||||
}
|
||||
|
||||
const { db, backend } = createDatabase(dbPath, options);
|
||||
return withOpenFailures(db, dbPath, () => DatabaseConnection.openOn(db, backend, dbPath, options));
|
||||
}
|
||||
|
||||
/** The rest of {@link open}: configure the connection, then migrate and repair a writable one. */
|
||||
private static openOn(
|
||||
db: SqliteDatabase,
|
||||
backend: SqliteBackend,
|
||||
dbPath: string,
|
||||
options: { readOnly?: boolean; deferIndexRecovery?: boolean },
|
||||
): DatabaseConnection {
|
||||
configureConnection(db, options.readOnly);
|
||||
|
||||
// Detect FTS5 availability for search fallback (#1532)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Node's built-in `node:sqlite`, loaded in one place, so that a runtime
|
||||
* without it fails with the one explanation that fits it: CodeGraph needs
|
||||
* Node.js 22.5+, or its own bundled runtime. Any other failure to open a
|
||||
* database is SQLite's own, and is reported as such (see ./open-error.ts).
|
||||
*/
|
||||
|
||||
/** The parts of `node:sqlite` CodeGraph uses. */
|
||||
export interface NodeSqliteModule {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
DatabaseSync: new (path: string, options?: { readOnly?: boolean }) => any;
|
||||
}
|
||||
|
||||
/** `node:sqlite` is missing or could not be loaded in this Node.js runtime. */
|
||||
export class NodeSqliteUnavailableError extends Error {
|
||||
constructor(cause: unknown) {
|
||||
const reason = cause instanceof Error ? cause.message : String(cause);
|
||||
super(
|
||||
`Node.js ${process.version} could not load its built-in SQLite module (${reason}).\n` +
|
||||
'CodeGraph requires node:sqlite (Node.js 22.5+). Install the self-contained CodeGraph ' +
|
||||
'release (it bundles a compatible Node), or run on Node 22.5+.',
|
||||
{ cause }
|
||||
);
|
||||
this.name = 'NodeSqliteUnavailableError';
|
||||
}
|
||||
}
|
||||
|
||||
/** Load `node:sqlite`, or throw {@link NodeSqliteUnavailableError}. */
|
||||
export function loadNodeSqlite(): NodeSqliteModule {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
return require('node:sqlite') as NodeSqliteModule;
|
||||
} catch (err) {
|
||||
throw new NodeSqliteUnavailableError(err);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Opening a CodeGraph database failed in SQLite itself: report what SQLite
|
||||
* said, with the database's path and a short hint at the usual cause. Before,
|
||||
* every such failure came out as "CodeGraph requires node:sqlite (Node.js
|
||||
* 22.5+)", which sent users after their Node version when the file was locked,
|
||||
* unreadable, or on a network share. That hint is now only for a runtime that
|
||||
* cannot load `node:sqlite` at all (./node-sqlite.ts); an index Windows and WSL
|
||||
* share keeps its own guidance (./wsl-shared-index.ts).
|
||||
*/
|
||||
|
||||
import { NodeSqliteUnavailableError } from './node-sqlite';
|
||||
import { toWslSharedIndexError, WslSharedIndexError } from './wsl-shared-index';
|
||||
|
||||
/** SQLite's primary result codes, by number. node:sqlite reports the extended code; its low byte is the primary one. */
|
||||
const SQLITE_CODE_NAMES: readonly string[] = [
|
||||
'SQLITE_OK', 'SQLITE_ERROR', 'SQLITE_INTERNAL', 'SQLITE_PERM', 'SQLITE_ABORT', 'SQLITE_BUSY',
|
||||
'SQLITE_LOCKED', 'SQLITE_NOMEM', 'SQLITE_READONLY', 'SQLITE_INTERRUPT', 'SQLITE_IOERR',
|
||||
'SQLITE_CORRUPT', 'SQLITE_NOTFOUND', 'SQLITE_FULL', 'SQLITE_CANTOPEN', 'SQLITE_PROTOCOL',
|
||||
'SQLITE_EMPTY', 'SQLITE_SCHEMA', 'SQLITE_TOOBIG', 'SQLITE_CONSTRAINT', 'SQLITE_MISMATCH',
|
||||
'SQLITE_MISUSE', 'SQLITE_NOLFS', 'SQLITE_AUTH', 'SQLITE_FORMAT', 'SQLITE_RANGE', 'SQLITE_NOTADB',
|
||||
];
|
||||
|
||||
/** What to check, for the failures an open meets in practice. */
|
||||
const HINTS: Readonly<Record<string, string>> = {
|
||||
SQLITE_BUSY: 'Another process has the database locked; retry when it is done.',
|
||||
SQLITE_PERM: 'Check that you can read and write the file and its folder.',
|
||||
SQLITE_READONLY: 'The database or its folder is read-only, and CodeGraph has to write its index there.',
|
||||
SQLITE_IOERR:
|
||||
'SQLite could not read or write the file. On a network share, or another filesystem without ' +
|
||||
'reliable file locking, keep the project and its index on a local disk.',
|
||||
SQLITE_CORRUPT: 'The database is damaged; run "codegraph index" to rebuild it.',
|
||||
SQLITE_FULL: 'The disk is full.',
|
||||
SQLITE_CANTOPEN:
|
||||
'Check that the folder exists and that you can read and write it, and that no other program ' +
|
||||
'(a backup or antivirus scan, say) holds the file open.',
|
||||
SQLITE_NOTADB: 'The file is not a SQLite database; move it aside and run "codegraph init" to build a new index.',
|
||||
};
|
||||
|
||||
/** A failure SQLite reported while opening the database at `dbPath`. */
|
||||
export class DatabaseOpenError extends Error {
|
||||
/** node:sqlite's error code ("ERR_SQLITE_ERROR"), kept from the original. */
|
||||
readonly code: unknown;
|
||||
/** SQLite's extended result code, kept from the original. */
|
||||
readonly errcode: unknown;
|
||||
readonly errstr: unknown;
|
||||
/** SQLite's name for the primary result code, e.g. "SQLITE_CANTOPEN". */
|
||||
readonly sqliteCode: string;
|
||||
readonly dbPath: string;
|
||||
|
||||
constructor(original: Error, dbPath: string) {
|
||||
const codes = original as Error & { code?: unknown; errcode?: unknown; errstr?: unknown };
|
||||
const primary = typeof codes.errcode === 'number' ? codes.errcode & 0xff : -1;
|
||||
const sqliteCode = SQLITE_CODE_NAMES[primary] ?? `SQLite error ${String(codes.errcode)}`;
|
||||
const hint = HINTS[sqliteCode];
|
||||
super(
|
||||
`Could not open the CodeGraph database at ${dbPath}: ${original.message} (${sqliteCode}).${hint ? ` ${hint}` : ''}`,
|
||||
{ cause: original }
|
||||
);
|
||||
this.name = 'DatabaseOpenError';
|
||||
this.code = codes.code;
|
||||
this.errcode = codes.errcode;
|
||||
this.errstr = codes.errstr;
|
||||
this.sqliteCode = sqliteCode;
|
||||
this.dbPath = dbPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error to throw for `err`, raised while opening the database at
|
||||
* `dbPath`: the Windows/WSL guidance for an index the two share, a
|
||||
* {@link DatabaseOpenError} for any other failure SQLite reported (it carries
|
||||
* a result code), and anything else unchanged, the missing-`node:sqlite`
|
||||
* explanation included.
|
||||
*/
|
||||
export function toDatabaseOpenError(err: unknown, dbPath: string): unknown {
|
||||
if (err instanceof DatabaseOpenError || err instanceof WslSharedIndexError || err instanceof NodeSqliteUnavailableError) {
|
||||
return err;
|
||||
}
|
||||
const shared = toWslSharedIndexError(err, dbPath);
|
||||
if (shared) return shared;
|
||||
if (err instanceof Error && typeof (err as { errcode?: unknown }).errcode === 'number') {
|
||||
return new DatabaseOpenError(err, dbPath);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* A project's index exists but could not be opened: what a caller reports as
|
||||
* that, not as "no index here". The Windows/WSL case has its own guidance.
|
||||
*/
|
||||
export function isDatabaseOpenFailure(err: unknown): err is DatabaseOpenError | NodeSqliteUnavailableError {
|
||||
return err instanceof DatabaseOpenError || err instanceof NodeSqliteUnavailableError;
|
||||
}
|
||||
@@ -10,6 +10,8 @@
|
||||
* wasm fallback. When run from source instead, it requires Node >= 22.5.
|
||||
*/
|
||||
|
||||
import { loadNodeSqlite } from './node-sqlite';
|
||||
import { toDatabaseOpenError } from './open-error';
|
||||
import { toWslSharedIndexError } from './wsl-shared-index';
|
||||
|
||||
export interface SqliteStatement {
|
||||
@@ -57,8 +59,7 @@ class NodeSqliteAdapter implements SqliteDatabase {
|
||||
private readonly _dbPath: string;
|
||||
|
||||
constructor(dbPath: string, opts?: { readOnly?: boolean }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
const { DatabaseSync } = loadNodeSqlite();
|
||||
this._dbPath = dbPath;
|
||||
this._db = opts?.readOnly ? new DatabaseSync(dbPath, { readOnly: true }) : new DatabaseSync(dbPath);
|
||||
}
|
||||
@@ -200,15 +201,8 @@ export function createDatabase(dbPath: string, opts?: { readOnly?: boolean }): {
|
||||
try {
|
||||
return { db: new NodeSqliteAdapter(dbPath, opts), backend: 'node-sqlite' };
|
||||
} catch (error) {
|
||||
// node:sqlite loaded and SQLite itself failed — not a missing-module case.
|
||||
const shared = toWslSharedIndexError(error, dbPath);
|
||||
if (shared) throw shared;
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(
|
||||
'Failed to open SQLite via the built-in node:sqlite module.\n' +
|
||||
'CodeGraph requires node:sqlite (Node.js 22.5+). Install the self-contained\n' +
|
||||
'CodeGraph release (it bundles a compatible Node), or run on Node 22.5+.\n' +
|
||||
`Underlying error: ${msg}`
|
||||
);
|
||||
// A runtime without node:sqlite says so (NodeSqliteUnavailableError);
|
||||
// anything SQLite itself reported names its code and the path.
|
||||
throw toDatabaseOpenError(error, dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -8,6 +8,7 @@ import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { isWslWindowsDrive } from './sync/watch-policy';
|
||||
import { loadNodeSqlite } from './db/node-sqlite';
|
||||
|
||||
/** The default per-project data directory name. */
|
||||
export const DEFAULT_CODEGRAPH_DIR = '.codegraph';
|
||||
@@ -214,10 +215,11 @@ function hasCodeGraphSchema(dbPath: string, st: fs.Stats): boolean {
|
||||
* second connection opened and closed here leaves the first one's locks alone.
|
||||
*/
|
||||
function probeSchema(dbPath: string): 'schema' | 'no-schema' | 'not-sqlite' | 'unknown' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
let db: any = null;
|
||||
try {
|
||||
// A runtime without node:sqlite can't inspect the file: `unknown`, so the
|
||||
// command goes on to open it and reports why that is impossible.
|
||||
const { DatabaseSync } = loadNodeSqlite();
|
||||
db = new DatabaseSync(dbPath, { readOnly: true });
|
||||
const row = db.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type = 'table' AND name = 'nodes'").get();
|
||||
return row !== undefined ? 'schema' : 'no-schema';
|
||||
|
||||
+13
-2
@@ -16,6 +16,8 @@ import type CodeGraph from '../index';
|
||||
import { resolveServerRoot } from '../directory';
|
||||
import { ToolHandler } from './tools';
|
||||
import { WslSharedIndexError } from '../db/wsl-shared-index';
|
||||
import { DatabaseOpenError, isDatabaseOpenFailure } from '../db/open-error';
|
||||
import type { NodeSqliteUnavailableError } from '../db/node-sqlite';
|
||||
import { assertNoRebuild, releaseWriterLock, tryAcquireWriterLock, writerLockHeldMessage } from './writer-lock';
|
||||
import { QueryPool, resolvePoolSize } from './query-pool';
|
||||
import { endFreshnessMeasurements } from './index-freshness';
|
||||
@@ -33,6 +35,15 @@ const loadCodeGraph = (): typeof import('../index').default =>
|
||||
/** How often the per-tool-call retry may re-run the sub-project down-scan. */
|
||||
const RETRY_SUBSCAN_TTL_MS = 5_000;
|
||||
|
||||
/**
|
||||
* Why the default project failed to open, when a tool call should say so
|
||||
* rather than "no project loaded": the Windows/WSL shared index (#995), or an
|
||||
* index that exists but SQLite could not open, or no node:sqlite at all.
|
||||
*/
|
||||
function reportableOpenFailure(err: unknown): WslSharedIndexError | DatabaseOpenError | NodeSqliteUnavailableError | null {
|
||||
return err instanceof WslSharedIndexError || isDatabaseOpenFailure(err) ? err : null;
|
||||
}
|
||||
|
||||
export interface MCPEngineOptions {
|
||||
/** Serve existing index contents without syncing, watching, or claiming a writer slot. */
|
||||
readOnly?: boolean;
|
||||
@@ -265,7 +276,7 @@ export class MCPEngine {
|
||||
}
|
||||
} catch (err) {
|
||||
// Still failing — caller will try again on the next tool call.
|
||||
this.toolHandler.setDefaultOpenFailure(err instanceof WslSharedIndexError ? err : null);
|
||||
this.toolHandler.setDefaultOpenFailure(reportableOpenFailure(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +407,7 @@ export class MCPEngine {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
process.stderr.write(`[CodeGraph MCP] Failed to open project at ${resolvedRoot}: ${msg}\n`);
|
||||
// The agent otherwise hears only "no project loaded" (#995).
|
||||
this.toolHandler.setDefaultOpenFailure(err instanceof WslSharedIndexError ? err : null);
|
||||
this.toolHandler.setDefaultOpenFailure(reportableOpenFailure(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-4
@@ -8,6 +8,8 @@ import type CodeGraph from '../index';
|
||||
import type { QueryPool } from './query-pool';
|
||||
import { findNearestCodeGraphRoot, isSameIndexRoot } from '../directory';
|
||||
import { WslSharedIndexError } from '../db/wsl-shared-index';
|
||||
import { DatabaseOpenError, isDatabaseOpenFailure } from '../db/open-error';
|
||||
import type { NodeSqliteUnavailableError } from '../db/node-sqlite';
|
||||
// Lazy-load the heavy CodeGraph chain off the MCP startup path — see the same
|
||||
// helper in engine.ts. ToolHandler must load to answer tools/list (static
|
||||
// schemas), but it must NOT drag in sqlite/query layers before the daemon binds;
|
||||
@@ -105,6 +107,20 @@ export class NotIndexedError extends Error {}
|
||||
* {@link NotIndexedError} it answers SUCCESS-shaped — never `isError`, which
|
||||
* would teach the agent to abandon codegraph for projects that work fine.
|
||||
*/
|
||||
/**
|
||||
* A project whose index exists but could not be opened: SQLite's own failure,
|
||||
* with the path and what to check, or a runtime without node:sqlite. A real
|
||||
* malfunction, so it stays `isError` with the retry-once note, but it says
|
||||
* what failed instead of calling it an internal error.
|
||||
*/
|
||||
function databaseOpenFailureText(err: DatabaseOpenError | NodeSqliteUnavailableError): string {
|
||||
return (
|
||||
`${err.message}\n` +
|
||||
'Retry the call once; if it persists, continue without codegraph for this task and pass ' +
|
||||
'the message above on to the user.'
|
||||
);
|
||||
}
|
||||
|
||||
function wslSharedIndexGuidance(err: WslSharedIndexError): string {
|
||||
return (
|
||||
`${err.message}\n\n` +
|
||||
@@ -2021,9 +2037,10 @@ export class ToolHandler {
|
||||
private knownSubprojects: string[] = [];
|
||||
private knownSubprojectsBase: string | null = null;
|
||||
// Why the default project failed to open, when that is worth telling the
|
||||
// agent instead of "no project loaded" — today only the Windows/WSL
|
||||
// shared-index error (#995). Engine-maintained; cleared by a successful open.
|
||||
private defaultOpenFailure: WslSharedIndexError | null = null;
|
||||
// agent instead of "no project loaded": the Windows/WSL shared-index error
|
||||
// (#995), or an index that exists but could not be opened (SQLite's own
|
||||
// failure, or no node:sqlite). Engine-maintained; cleared by a successful open.
|
||||
private defaultOpenFailure: WslSharedIndexError | DatabaseOpenError | NodeSqliteUnavailableError | null = null;
|
||||
// Per-start-path cache of the git worktree/index mismatch (issue #155). The
|
||||
// mismatch is a fixed property of (where the request came from → which
|
||||
// .codegraph/ it resolves to), so the up-to-two `git rev-parse` spawns run
|
||||
@@ -2083,7 +2100,7 @@ export class ToolHandler {
|
||||
* call that needs it answers with that fix rather than "no project loaded".
|
||||
* `null` clears it.
|
||||
*/
|
||||
setDefaultOpenFailure(err: WslSharedIndexError | null): void {
|
||||
setDefaultOpenFailure(err: WslSharedIndexError | DatabaseOpenError | NodeSqliteUnavailableError | null): void {
|
||||
this.defaultOpenFailure = err;
|
||||
}
|
||||
|
||||
@@ -3021,6 +3038,10 @@ export class ToolHandler {
|
||||
if (err instanceof PathRefusalError) {
|
||||
return this.errorResult(err.message);
|
||||
}
|
||||
// An index that exists but could not be opened: a malfunction, said as what it is.
|
||||
if (isDatabaseOpenFailure(err)) {
|
||||
return this.errorResult(databaseOpenFailureText(err));
|
||||
}
|
||||
return this.errorResult(
|
||||
`Tool execution failed: ${err instanceof Error ? err.message : String(err)}. ` +
|
||||
'This is an internal codegraph error — retry the call once; if it persists, ' +
|
||||
@@ -3109,6 +3130,9 @@ export class ToolHandler {
|
||||
if (err instanceof PathRefusalError) {
|
||||
return this.errorResult(err.message);
|
||||
}
|
||||
if (isDatabaseOpenFailure(err)) {
|
||||
return this.errorResult(databaseOpenFailureText(err));
|
||||
}
|
||||
return this.errorResult(
|
||||
`Tool execution failed: ${err instanceof Error ? err.message : String(err)}. ` +
|
||||
'This is an internal codegraph error — retry the call once; if it persists, ' +
|
||||
|
||||
Reference in New Issue
Block a user