ruvnet/ruflo · error

No query executor configured

Error message

No query executor configured

What it means

Thrown by AttentionExecutor.executeSQL() (attention-executor.ts:250) when the class has no SQL runner bound. The executor generates PostgreSQL RuVector SQL for attention mechanisms and needs a callback of type (sql: string) => Promise<unknown> to send that SQL to a database; it is stored as nullable and only assigned through setQueryExecutor(). Note that the public execute() method guards with 'if (this.queryExecutor)' and otherwise falls back to local computeBatch(), so this throw fires on code paths that call executeSQL() directly (e.g. subclasses or custom execution paths) without ever binding an executor.

Source

Thrown at v3/@claude-flow/plugins/src/integrations/ruvector/attention-executor.ts:250

  /**
   * Generate SQL without execution.
   */
  generateSQL(mechanism: AttentionMechanism, input: AttentionInput): string {
    return this.registry.get(mechanism).toSQL(input);
  }

  /**
   * Generate batch SQL.
   */
  generateBatchSQL(mechanism: AttentionMechanism, inputs: AttentionInput[]): string {
    const sqls = inputs.map(input => this.generateSQL(mechanism, input));
    return `WITH batch_attention AS (\n  ${sqls.join(',\n  ')}\n)\nSELECT * FROM batch_attention;`;
  }

  private async executeSQL(sql: string, timeoutMs?: number): Promise<unknown> {
    if (!this.queryExecutor) {
      throw new Error('No query executor configured');
    }

    if (timeoutMs) {
      return Promise.race([
        this.queryExecutor(sql),
        new Promise((_, reject) =>
          setTimeout(() => reject(new Error('Query timeout')), timeoutMs)
        ),
      ]);
    }

    return this.queryExecutor(sql);
  }

  private parseResult(result: unknown): number[][] {
    if (Array.isArray(result)) {
      return result.map(row => {
        if (Array.isArray(row)) return row;

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Call executor.setQueryExecutor(sql => pool.query(sql)) once after construction, before any execution
  2. Verify you are not on a dryRun path: execute() with dryRun:true returns early with SQL only, but custom executeSQL callers still need the binding
  3. If you extended AttentionExecutor, ensure your override path either sets an executor or uses the local computeBatch fallback like execute() does
  4. Add a startup assertion that logs/throws early when the executor is meant to run against PostgreSQL but no query executor was injected

Example fix

// before
const executor = new AttentionExecutor(createDefaultRegistry());
const result = await executor.execute('self', input); // fine (local fallback)
await runSqlDirectly(executor); // throws: No query executor configured

// after
const executor = new AttentionExecutor(createDefaultRegistry());
executor.setQueryExecutor(sql => pool.query(sql));
const result = await executor.execute('self', input);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the executor is bound to a SQL runner before any DB-backed execution.
// Note: AttentionExecutor has no public isConfigured(); track it at the call site.
let sqlBound = false;
function bindExecutor(executor: AttentionExecutor, pool: { query: (sql: string) => Promise<unknown> }) {
  executor.setQueryExecutor(sql => pool.query(sql));
  sqlBound = true;
}
function assertSqlReady() {
  if (!sqlBound) throw new Error('AttentionExecutor used before setQueryExecutor()');
}

Try / catch

try {
  const result = await executor.execute(mechanism, input);
} catch (err) {
  if (err instanceof Error && err.message === 'No query executor configured') {
    // bind a runner and retry, or degrade to the local computeBatch path
    executor.setQueryExecutor(sql => pool.query(sql));
    return executor.execute(mechanism, input);
  }
  throw err;
}

Prevention

When it happens

Trigger: Constructing new AttentionExecutor(registry) and invoking a code path that reaches executeSQL() without first calling setQueryExecutor(fn); subclassing AttentionExecutor and calling the private executeSQL via ts-ignore/cast; binding the executor conditionally (only when a pool exists) so some runtime paths skip it.

Common situations: Wiring RuVector attention into a new app and forgetting to pass the pg Pool query function; DI container creating the executor but not the SQL runner; test harness using dryRun:false without a mock executor; refactors that move setQueryExecutor behind an env check that evaluates false.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/efaf947efa84f13d. Report an issue: GitHub.