{"record":{"id":"d5f89449e377c64d","repo":"canopy-network/canopy","slug":"errinvalidpluginschema","errorCode":"ErrInvalidPluginSchema","errorMessage":"empty message name in type url %q","messagePattern":"empty message name in type url %q","errorType":"error_code","errorClass":"ErrInvalidPluginSchema","httpStatus":null,"severity":"error","filePath":"lib/codec.go","lineNumber":150,"sourceCode":"\n\t// Unmarshal the file protos into a 'proto.Files' object\n\tfiles, err := protodesc.NewFiles(&descriptorpb.FileDescriptorSet{File: fileProtos})\n\tif err != nil {\n\t\treturn ErrInvalidPluginSchema(err)\n\t}\n\n\tbyFullyQualifiedName := make(map[string]protoreflect.MessageDescriptor)\n\tbyTypeURL := make(map[string]protoreflect.MessageDescriptor)\n\tbyCommonMessageName := make(map[string]protoreflect.MessageDescriptor)\n\n\t// for each transaction type URL - register it\n\tfor _, typeURL := range config.TransactionTypeUrls {\n\t\tname := typeURL\n\t\tif idx := strings.LastIndex(typeURL, \"/\"); idx >= 0 {\n\t\t\tname = typeURL[idx+1:]\n\t\t}\n\t\tif name == \"\" {\n\t\t\treturn ErrInvalidPluginSchema(fmt.Errorf(\"empty message name in type url %q\", typeURL))\n\t\t}\n\t\tdesc, e := files.FindDescriptorByName(protoreflect.FullName(name))\n\t\tif e != nil {\n\t\t\treturn ErrInvalidPluginSchema(fmt.Errorf(\"message %s: %w\", name, e))\n\t\t}\n\t\tmd, ok := desc.(protoreflect.MessageDescriptor)\n\t\tif !ok {\n\t\t\treturn ErrInvalidPluginSchema(fmt.Errorf(\"descriptor %s is not a message\", name))\n\t\t}\n\t\tbyFullyQualifiedName[name] = md\n\t\tbyTypeURL[typeURL] = md\n\t}\n\n\t// for each transaction (message) common name - register it\n\tif len(config.SupportedTransactions) > 0 {\n\t\tif len(config.SupportedTransactions) != len(config.TransactionTypeUrls) {\n\t\t\treturn ErrInvalidPluginSchema(fmt.Errorf(\"supported transactions count %d does not match transaction type urls count %d\", len(config.SupportedTransactions), len(config.TransactionTypeUrls)))\n\t\t}","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/lib/codec.go#L132-L168","documentation":"Register validates a plugin codec config's TransactionTypeUrls. Each URL is stripped to its last path segment; if nothing remains after the '/' (or the URL was empty), the derived proto message name is empty and registration fails with ErrInvalidPluginSchema.","triggerScenarios":"Plugin config lists a TransactionTypeUrl that is empty (\"\") or ends with '/' so the trailing name after LastIndex(\"/\") is empty.","commonSituations":"Hand-edited plugin config with a typo like \"type.googleapis.com/\" ; templated config where the message name variable was empty; truncated URL from an env var or CLI flag.","solutions":["Set a full type URL ending in a non-empty message name, e.g. \"type.googleapis.com/myplugin.v1.Send\"","Remove the trailing '/' or empty entries from config.TransactionTypeUrls","Validate the plugin config before loading it into the codec"],"exampleFix":"// before\n\"transactionTypeUrls\": [\"type.googleapis.com/\"]\n// after\n\"transactionTypeUrls\": [\"type.googleapis.com/myplugin.v1.Send\"]","handlingStrategy":"validation","validationCode":"for _, u := range cfg.TransactionTypeUrls {\n    name := u[strings.LastIndex(u, \"/\")+1:]\n    if name == \"\" { return fmt.Errorf(\"bad type url %q\", u) }\n}","typeGuard":"func validTypeURL(u string) bool {\n    i := strings.LastIndex(u, \"/\")\n    return i >= 0 && i < len(u)-1\n}","tryCatchPattern":"if err := codec.Register(cfg); err != nil {\n    var schemaErr *fsm.ErrInvalidPluginSchema\n    if errors.As(err, &schemaErr) { /* fail plugin load with clear config error */ }\n    return err\n}","preventionTips":["Validate type URLs end with a message name before writing plugin config","Avoid trailing slashes in type URLs","Lint plugin configs at startup"],"tags":["plugin","schema","type-url","config"],"backgroundTag":"schema-validation-failed","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}