{"record":{"id":"0c047945266a071e","repo":"vxcontrol/pentagi","slug":"definitions-and-handlers-must-have-the-same-length","errorCode":null,"errorMessage":"definitions and handlers must have the same length","messagePattern":"definitions and handlers must have the same length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/tools.go","lineNumber":773,"sourceCode":"\t\t// the store owns its own connection (no shared pool configured).\n\t\tif fte.cfg.PgxPool == nil {\n\t\t\tfte.store.Close()\n\t\t}\n\t\tfte.store = nil\n\t}\n\n\t// TODO: here better to get flow containers list and purge all of them\n\tif err := fte.docker.RemoveContainer(ctx, fte.primaryLID, fte.primaryID); err != nil {\n\t\tcontainerName := PrimaryTerminalName(fte.cfg.TenantPrefix(), fte.flowID)\n\t\treturn fmt.Errorf(\"failed to purge container '%s': %w\", containerName, err)\n\t}\n\n\treturn nil\n}\n\nfunc (fte *flowToolsExecutor) GetCustomExecutor(cfg CustomExecutorConfig) (ContextToolsExecutor, error) {\n\tif len(cfg.Definitions) != len(cfg.Handlers) {\n\t\treturn nil, fmt.Errorf(\"definitions and handlers must have the same length\")\n\t}\n\n\tfor _, def := range cfg.Definitions {\n\t\tif _, ok := cfg.Handlers[def.Name]; !ok {\n\t\t\treturn nil, fmt.Errorf(\"handler for function %s not found\", def.Name)\n\t\t}\n\t}\n\n\tfor _, builtin := range cfg.Builtin {\n\t\tif def, ok := fte.definitions[builtin]; !ok {\n\t\t\treturn nil, fmt.Errorf(\"builtin function %s not found\", builtin)\n\t\t} else {\n\t\t\tcfg.Definitions = append(cfg.Definitions, def)\n\t\t\tcfg.Handlers[builtin] = fte.handlers[builtin]\n\t\t}\n\t}\n\n\tbarriers := make(map[string]struct{})","sourceCodeStart":755,"sourceCodeEnd":791,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/tools.go#L755-L791","documentation":"GetCustomExecutor validates CustomExecutorConfig before building a custom tools executor: the number of tool Definitions must equal the number of entries in the Handlers map... actually it requires len(Definitions) == len(Handlers) as a quick sanity check that every definition has exactly one handler and no extras. Mismatched lengths indicate a miswired tool registration.","triggerScenarios":"Calling GetCustomExecutor with a CustomExecutorConfig where cfg.Definitions has N entries but cfg.Handlers map has a different count — e.g. two definitions share the same name (map collapses them) or a handler was added without a corresponding definition (and vice versa).","commonSituations":"Registering custom agent tools where a tool definition and its handler function drift out of sync after adding/removing a tool; duplicate FunctionDefinition names collapsing in the Handlers map; forgetting to add the handler for a newly defined function.","solutions":["Ensure every entry in cfg.Definitions has a unique Name and a matching key in cfg.Handlers.","Build Definitions and Handlers together from a single source (e.g. a slice of {def, handler} pairs) so they cannot diverge.","Log len(cfg.Definitions) and len(cfg.Handlers) before the call to spot the mismatch."],"exampleFix":"// before\ncfg := tools.CustomExecutorConfig{\n    Definitions: []tools.FunctionDefinition{defA, defB},\n    Handlers:    map[string]tools.Handler{\"a\": hA}, // missing b\n}\n\n// after\ncfg := tools.CustomExecutorConfig{\n    Definitions: []tools.FunctionDefinition{defA, defB},\n    Handlers:    map[string]tools.Handler{\"a\": hA, \"b\": hB},\n}","handlingStrategy":"validation","validationCode":"if len(cfg.Definitions) != len(cfg.Handlers) {\n    return fmt.Errorf(\"definitions=%d handlers=%d: each definition needs exactly one handler\",\n        len(cfg.Definitions), len(cfg.Handlers))\n}\nif err := isUniqueNames(cfg.Definitions); err != nil {\n    return err // duplicate names collapse in the Handlers map\n}","typeGuard":"func validCustomConfig(cfg tools.CustomExecutorConfig) bool {\n    if len(cfg.Definitions) != len(cfg.Handlers) {\n        return false\n    }\n    seen := map[string]struct{}{}\n    for _, d := range cfg.Definitions {\n        if _, dup := seen[d.Name]; dup {\n            return false\n        }\n        seen[d.Name] = struct{}{}\n    }\n    return true\n}","tryCatchPattern":"executor, err := flowTools.GetCustomExecutor(cfg)\nif err != nil {\n    return fmt.Errorf(\"invalid custom tool config: %w\", err)\n}","preventionTips":["Define custom tools as a single []ToolSpec{{Def, Handler}} slice and derive Definitions/Handlers from it.","Keep function definition names unique — duplicate names collapse the handler map and break the length invariant.","Add a unit test asserting GetCustomExecutor succeeds for every shipped tool config."],"tags":["go","validation","tools-api"],"backgroundTag":"schema-validation-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}