{"record":{"id":"fa46b74d2a75620d","repo":"opendataloader-project/opendataloader-pdf","slug":"hybrid-backend-type-cannot-be-null-or-empty","errorCode":null,"errorMessage":"Hybrid backend type cannot be null or empty","messagePattern":"Hybrid backend type cannot be null or empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HybridClientFactory.java","lineNumber":81,"sourceCode":"    private HybridClientFactory() {\n        // Private constructor to prevent instantiation\n    }\n\n    /**\n     * Gets or creates a hybrid client for the specified backend.\n     *\n     * <p>Clients are cached and reused across multiple documents to avoid\n     * creating new thread pools for each document. Call {@link #shutdown()}\n     * when processing is complete to release resources.\n     *\n     * @param hybrid The backend type (e.g., \"docling\", \"hancom\", \"azure\", \"google\").\n     * @param config The configuration for the hybrid client.\n     * @return A HybridClient instance for the specified backend.\n     * @throws IllegalArgumentException If the backend type is unknown or not supported.\n     */\n    public static HybridClient getOrCreate(String hybrid, HybridConfig config) {\n        if (hybrid == null || hybrid.isEmpty()) {\n            throw new IllegalArgumentException(\"Hybrid backend type cannot be null or empty\");\n        }\n\n        String lowerHybrid = hybrid.toLowerCase();\n\n        return CLIENT_CACHE.computeIfAbsent(lowerHybrid, key -> createClient(key, config));\n    }\n\n    /**\n     * Creates a new hybrid client instance.\n     */\n    private static HybridClient createClient(String hybrid, HybridConfig config) {\n        if (BACKEND_DOCLING_FAST.equals(hybrid)) {\n            return new DoclingFastServerClient(config);\n        } else if (BACKEND_HANCOM.equals(hybrid)) {\n            return new HancomClient(config);\n        } else if (BACKEND_HANCOM_AI.equals(hybrid)) {\n            return new HancomAIClient(config);\n        } else if (BACKEND_AZURE.equals(hybrid)) {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HybridClientFactory.java#L63-L99","documentation":"HybridClientFactory.getOrCreate() requires a non-null, non-empty backend type string. This is a programming-error guard — the backend name selects which HybridClient implementation to instantiate and cache. A null or empty string cannot match any backend constant and is rejected before the cache lookup to avoid a confusing NullPointerException downstream.","triggerScenarios":"Calling HybridClientFactory.getOrCreate(null, config) or HybridClientFactory.getOrCreate(\"\", config). This typically originates from a missing --hybrid CLI argument value, a null returned by a configuration parser, or an unset environment variable that was expected to provide the backend name.","commonSituations":"CLI flag --hybrid is provided without a value (or with trailing space stripped); the backend name is read from a config file/JSON that has a null or missing 'hybrid' key; programmatic integration code passes a variable that was never assigned; the value comes from an environment variable that was not set in the deployment.","solutions":["Provide a valid backend name: one of 'docling-fast', 'hancom', 'hancom-ai' (see HybridClientFactory constants).","Check the CLI invocation — ensure --hybrid has a value: `--hybrid hancom`, not just `--hybrid`.","If the backend name comes from configuration, validate it is present before calling getOrCreate.","Use HybridClientFactory.getSupportedBackends() to list valid options programmatically."],"exampleFix":"// before: unvalidated external input passed directly\nString backend = System.getenv(\"HYBRID_BACKEND\");\nHybridClient client = HybridClientFactory.getOrCreate(backend, config);\n\n// after: validate before calling\nString backend = System.getenv(\"HYBRID_BACKEND\");\nif (backend == null || backend.isBlank()) {\n    throw new IllegalArgumentException(\n        \"HYBRID_BACKEND env var is not set. Valid options: \"\n        + HybridClientFactory.getSupportedBackends());\n}\nHybridClient client = HybridClientFactory.getOrCreate(backend, config);","handlingStrategy":"validation","validationCode":"String backend = getConfiguredBackend(); // from CLI, env, or config file\nif (backend == null || backend.isBlank()) {\n    throw new IllegalArgumentException(\n        \"Hybrid backend type is required. Valid options: \"\n        + HybridClientFactory.getSupportedBackends());\n}\nHybridClient client = HybridClientFactory.getOrCreate(backend, config);","typeGuard":"// Check that the backend string is non-empty before calling the factory\npublic static boolean isValidBackendName(String backend) {\n    return backend != null && !backend.isBlank();\n}","tryCatchPattern":"try {\n    client = HybridClientFactory.getOrCreate(backend, config);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"cannot be null or empty\")) {\n        // Programming error — fix the caller, not a runtime retry\n        throw new IllegalStateException(\"Backend type was not provided by configuration source\", e);\n    }\n    throw e;\n}","preventionTips":["Validate the backend string at the configuration boundary (CLI parser, config loader) before it reaches the factory.","Provide a clear default backend or require an explicit choice with a helpful error message.","Use HybridClientFactory.getSupportedBackends() to display valid options in error messages.","Never pass unchecked user input or environment variables directly to getOrCreate without null/empty validation."],"tags":["configuration","validation","hybrid","factory","programming-error"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}