ruvnet/ruflo · error

Invalid numHeads: ${this.config.numHeads}. Must be positive.

Error message

Invalid numHeads: ${this.config.numHeads}. Must be positive.

What it means

BaseGNNLayer.validateConfig() (gnn.ts:491) requires numHeads to be a positive integer when supplied, because attention-head layers (GAT, GATv2, HGT, etc.) split outputDim across heads and cannot operate with zero or negative heads. The check only runs when numHeads is defined; other layer types that ignore numHeads are unaffected.

Source

Thrown at v3/@claude-flow/plugins/src/integrations/ruvector/gnn.ts:491

    this.validateConfig();
  }

  /**
   * Validate layer configuration.
   * @throws Error if configuration is invalid
   */
  protected validateConfig(): void {
    if (this.config.inputDim <= 0) {
      throw new Error(`Invalid inputDim: ${this.config.inputDim}. Must be positive.`);
    }
    if (this.config.outputDim <= 0) {
      throw new Error(`Invalid outputDim: ${this.config.outputDim}. Must be positive.`);
    }
    if (this.config.dropout !== undefined && (this.config.dropout < 0 || this.config.dropout > 1)) {
      throw new Error(`Invalid dropout: ${this.config.dropout}. Must be between 0 and 1.`);
    }
    if (this.config.numHeads !== undefined && this.config.numHeads <= 0) {
      throw new Error(`Invalid numHeads: ${this.config.numHeads}. Must be positive.`);
    }
  }

  abstract forward(graph: GraphData): Promise<GNNOutput>;
  abstract messagePass(nodes: NodeFeatures, edges: EdgeFeatures): Promise<NodeFeatures>;

  /**
   * Aggregate messages using the specified method.
   */
  async aggregate(messages: Message[], method: AggregationMethod): Promise<number[]> {
    if (messages.length === 0) {
      return new Array(this.config.outputDim).fill(0);
    }

    const vectors = messages.map((m) => m.vector);
    const weights = messages.map((m) => m.weight ?? 1);

    switch (method) {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Set numHeads to a positive integer (common values: 1, 2, 4, 8) that divides outputDim evenly
  2. When deriving heads from dimensions, assert (outputDim % numHeads === 0 && numHeads > 0) first
  3. Treat 0/undefined in external config as 'use library default' and strip the field before construction

Example fix

// before
const heads = Math.floor(64 / 128); // 0 -> throws in constructor
const layer = new GATLayer({ type: 'gat', inputDim: 128, outputDim: 64, numHeads: heads });

// after
const heads = 4; // 64 / 4 = 16-dim heads
const layer = new GATLayer({ type: 'gat', inputDim: 128, outputDim: 64, numHeads: heads });
Defensive patterns

Strategy: validation

Validate before calling

if (cfg.numHeads !== undefined && !(Number.isInteger(cfg.numHeads) && cfg.numHeads > 0)) {
  throw new TypeError(`numHeads must be a positive integer, got ${cfg.numHeads}`);
}
if (cfg.numHeads !== undefined && cfg.outputDim !== undefined && cfg.outputDim % cfg.numHeads !== 0) {
  throw new RangeError(`outputDim ${cfg.outputDim} not divisible by numHeads ${cfg.numHeads}`);
}
const layer = registry.createLayer('gat', cfg);

Type guard

function isValidNumHeads(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && v > 0;
}

Try / catch

try {
  layer = new GATLayer(config);
} catch (err) {
  if (err instanceof Error && err.message.includes('Invalid numHeads')) {
    delete config.numHeads; // fall back to library default
    layer = new GATLayer(config);
  } else throw err;
}

Prevention

When it happens

Trigger: new GATLayer({ type: 'gat', numHeads: 0, ... }); deriving numHeads from outputDim / headDim where headDim > outputDim yields 0; config defaults of 0 copied from a template; fractional heads (0.5) also fail the <= 0 check path only when non-positive, so 0 and negatives throw.

Common situations: Computing heads = outputDim // headDim with mismatched dims; YAML/JSON config with numHeads: 0 as 'unset' placeholder; tuning scripts sweeping down to 0.

Related errors


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