{"record":{"id":"e5dff15bc1bb1f7d","repo":"apache/pulsar","slug":"invalid-fully-qualified-function-name","errorCode":null,"errorMessage":"Invalid Fully Qualified Function Name ","messagePattern":"Invalid Fully Qualified Function Name ","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionCommon.java","lineNumber":350,"sourceCode":"\n    public static String extractTenantFromFullyQualifiedName(String fqfn) {\n        return extractFromFullyQualifiedName(fqfn, 0);\n    }\n\n    public static String extractNamespaceFromFullyQualifiedName(String fqfn) {\n        return extractFromFullyQualifiedName(fqfn, 1);\n    }\n\n    public static String extractNameFromFullyQualifiedName(String fqfn) {\n        return extractFromFullyQualifiedName(fqfn, 2);\n    }\n\n    private static String extractFromFullyQualifiedName(String fqfn, int index) {\n        String[] parts = fqfn.split(\"/\");\n        if (parts.length >= 3) {\n            return parts[index];\n        }\n        throw new RuntimeException(\"Invalid Fully Qualified Function Name \" + fqfn);\n    }\n\n    public static double roundDecimal(double value, int places) {\n        double scale = Math.pow(10, places);\n        return Math.round(value * scale) / scale;\n    }\n\n    public static String capFirstLetter(Enum<?> en) {\n        return StringUtils.capitalize(en.toString().toLowerCase());\n    }\n\n    public static boolean isFunctionCodeBuiltin(\n            FunctionDetails functionDetail) {\n        return isFunctionCodeBuiltin(functionDetail, functionDetail.getComponentType());\n    }\n\n    public static boolean isFunctionCodeBuiltin(\n            FunctionDetails functionDetails,","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionCommon.java#L332-L368","documentation":"FunctionCommon's fully-qualified function name (FQFN) helpers expect the canonical 'tenant/namespace/functionName' format with at least three '/'-separated parts. extractFromFullyQualifiedName throws this RuntimeException when the input string has fewer than three parts, i.e. it is not a valid FQFN.","triggerScenarios":"Calling extractTenantFromFullyQualifiedName / extractNamespaceFromFullyQualifiedName / extractNameFromFullyQualifiedName with strings like 'my-function', 'tenant/namespace', an empty string, or a topic/subject name mistakenly passed instead of a function FQFN.","commonSituations":"Parsing user-provided CLI arguments where only the function name was given; passing a topic name (containing ':' or different separators) instead of a function name; passing a Pulsar topic or sink/source name that is tenant/namespace-only; legacy data written in a different format.","solutions":["Ensure the string has the form tenant/namespace/functionName before calling the extractors","Build the FQFN from components with FunctionCommon.getFullyQualifiedName(tenant, namespace, name) when you only have parts","Add a precondition check (split('/') >= 3) in your calling code and produce a clearer error message","Verify the input is a function FQFN and not a topic or connector name"],"exampleFix":"// before\nString name = \"my-function\";\nString tenant = FunctionCommon.extractTenantFromFullyQualifiedName(name); // throws\n// after\nString name = \"public/default/my-function\";\nString tenant = FunctionCommon.extractTenantFromFullyQualifiedName(name); // \"public\"","handlingStrategy":"validation","validationCode":"// Pre-validate FQFN shape\nboolean validFqfn(String fqfn) {\n  String[] parts = fqfn == null ? new String[0] : fqfn.split(\"/\");\n  return parts.length >= 3 && !parts[0].isEmpty() && !parts[1].isEmpty() && !parts[2].isEmpty();\n}\nif (!validFqfn(input)) throw new IllegalArgumentException(\"Expected tenant/namespace/functionName, got: \" + input);","typeGuard":"static boolean isFqfn(String s) {\n  if (s == null) return false;\n  String[] p = s.split(\"/\");\n  return p.length >= 3 && !p[0].isEmpty() && !p[1].isEmpty() && !p[2].isEmpty();\n}","tryCatchPattern":"try {\n  String tenant = FunctionCommon.extractTenantFromFullyQualifiedName(fqfn);\n} catch (RuntimeException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Invalid Fully Qualified Function Name\")) {\n    throw new IllegalArgumentException(\"Pass tenant/namespace/functionName, got: \" + fqfn);\n  }\n  throw e;\n}","preventionTips":["Build FQFNs with FunctionCommon.getFullyQualifiedName(tenant, namespace, name) instead of string concatenation","Distinguish function FQFNs from topic names in your config schema","Validate user CLI input format before passing to extractors","Keep a single helper for FQFN parsing to avoid format drift"],"tags":["pulsar-functions","parsing","fqfn","validation"],"backgroundTag":"malformed-identifier","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}