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
- Set numHeads to a positive integer (common values: 1, 2, 4, 8) that divides outputDim evenly
- When deriving heads from dimensions, assert (outputDim % numHeads === 0 && numHeads > 0) first
- 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
- Choose numHeads from {1, 2, 4, 8} and keep outputDim evenly divisible by it
- When deriving heads from dims (heads = outputDim / headDim), assert headDim <= outputDim first
- Strip zero-valued 'unset' placeholders from loaded configs before construction
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
- Invalid inputDim: ${this.config.inputDim}. Must be positive.
- Invalid outputDim: ${this.config.outputDim}. Must be positiv
- Agent config must include id, name, and type
- Worker config must include id
- Invalid dropout: ${this.config.dropout}. Must be between 0 a
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/a2eb98314df11ed1.
Report an issue: GitHub.