{"record":{"id":"a2eb98314df11ed1","repo":"ruvnet/ruflo","slug":"invalid-numheads-this-config-numheads-must-be","errorCode":null,"errorMessage":"Invalid numHeads: ${this.config.numHeads}. Must be positive.","messagePattern":"Invalid numHeads: (.+?)\\. Must be positive\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/integrations/ruvector/gnn.ts","lineNumber":491,"sourceCode":"    this.validateConfig();\n  }\n\n  /**\n   * Validate layer configuration.\n   * @throws Error if configuration is invalid\n   */\n  protected validateConfig(): void {\n    if (this.config.inputDim <= 0) {\n      throw new Error(`Invalid inputDim: ${this.config.inputDim}. Must be positive.`);\n    }\n    if (this.config.outputDim <= 0) {\n      throw new Error(`Invalid outputDim: ${this.config.outputDim}. Must be positive.`);\n    }\n    if (this.config.dropout !== undefined && (this.config.dropout < 0 || this.config.dropout > 1)) {\n      throw new Error(`Invalid dropout: ${this.config.dropout}. Must be between 0 and 1.`);\n    }\n    if (this.config.numHeads !== undefined && this.config.numHeads <= 0) {\n      throw new Error(`Invalid numHeads: ${this.config.numHeads}. Must be positive.`);\n    }\n  }\n\n  abstract forward(graph: GraphData): Promise<GNNOutput>;\n  abstract messagePass(nodes: NodeFeatures, edges: EdgeFeatures): Promise<NodeFeatures>;\n\n  /**\n   * Aggregate messages using the specified method.\n   */\n  async aggregate(messages: Message[], method: AggregationMethod): Promise<number[]> {\n    if (messages.length === 0) {\n      return new Array(this.config.outputDim).fill(0);\n    }\n\n    const vectors = messages.map((m) => m.vector);\n    const weights = messages.map((m) => m.weight ?? 1);\n\n    switch (method) {","sourceCodeStart":473,"sourceCodeEnd":509,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/integrations/ruvector/gnn.ts#L473-L509","documentation":"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.","triggerScenarios":"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.","commonSituations":"Computing heads = outputDim // headDim with mismatched dims; YAML/JSON config with numHeads: 0 as 'unset' placeholder; tuning scripts sweeping down to 0.","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"],"exampleFix":"// before\nconst heads = Math.floor(64 / 128); // 0 -> throws in constructor\nconst layer = new GATLayer({ type: 'gat', inputDim: 128, outputDim: 64, numHeads: heads });\n\n// after\nconst heads = 4; // 64 / 4 = 16-dim heads\nconst layer = new GATLayer({ type: 'gat', inputDim: 128, outputDim: 64, numHeads: heads });","handlingStrategy":"validation","validationCode":"if (cfg.numHeads !== undefined && !(Number.isInteger(cfg.numHeads) && cfg.numHeads > 0)) {\n  throw new TypeError(`numHeads must be a positive integer, got ${cfg.numHeads}`);\n}\nif (cfg.numHeads !== undefined && cfg.outputDim !== undefined && cfg.outputDim % cfg.numHeads !== 0) {\n  throw new RangeError(`outputDim ${cfg.outputDim} not divisible by numHeads ${cfg.numHeads}`);\n}\nconst layer = registry.createLayer('gat', cfg);","typeGuard":"function isValidNumHeads(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0;\n}","tryCatchPattern":"try {\n  layer = new GATLayer(config);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Invalid numHeads')) {\n    delete config.numHeads; // fall back to library default\n    layer = new GATLayer(config);\n  } else throw err;\n}","preventionTips":["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"],"tags":["gnn","validation","attention-heads","constructor"],"backgroundTag":"argument-validation-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}