{"record":{"id":"8ee63a0e5529ef24","repo":"apache/flink","slug":"cannot-compare-specified-resources-with-unknown-re","errorCode":null,"errorMessage":"Cannot compare specified resources with UNKNOWN resources.","messagePattern":"Cannot compare specified resources with UNKNOWN resources\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/common/operators/ResourceSpec.java","lineNumber":241,"sourceCode":"            throw new UnsupportedOperationException();\n        }\n    }\n\n    /**\n     * Checks the current resource less than or equal with the other resource by comparing all the\n     * fields in the resource.\n     *\n     * @param other The resource to compare\n     * @return True if current resource is less than or equal with the other resource, otherwise\n     *     return false.\n     */\n    public boolean lessThanOrEqual(final ResourceSpec other) {\n        checkNotNull(other, \"Cannot compare with null resources\");\n\n        if (this.equals(UNKNOWN) && other.equals(UNKNOWN)) {\n            return true;\n        } else if (this.equals(UNKNOWN) || other.equals(UNKNOWN)) {\n            throw new IllegalArgumentException(\n                    \"Cannot compare specified resources with UNKNOWN resources.\");\n        }\n\n        int cmp1 = this.cpuCores.getValue().compareTo(other.getCpuCores().getValue());\n        int cmp2 = this.taskHeapMemory.compareTo(other.taskHeapMemory);\n        int cmp3 = this.taskOffHeapMemory.compareTo(other.taskOffHeapMemory);\n        int cmp4 = this.managedMemory.compareTo(other.managedMemory);\n        if (cmp1 <= 0 && cmp2 <= 0 && cmp3 <= 0 && cmp4 <= 0) {\n            for (ExternalResource resource : extendedResources.values()) {\n                if (!other.extendedResources.containsKey(resource.getName())\n                        || other.extendedResources\n                                        .get(resource.getName())\n                                        .getValue()\n                                        .compareTo(resource.getValue())\n                                < 0) {\n                    return false;\n                }\n            }","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/common/operators/ResourceSpec.java#L223-L259","documentation":"Thrown by ResourceSpec.lessThanOrEqual() when exactly one of the two compared ResourceSpec instances is the UNKNOWN constant (and the other is a concrete resource spec). UNKNOWN is a sentinel meaning 'resource requirements not specified'; Flink treats it as semantically uncomparable because you cannot assert a concrete spec is <= an unknown one or vice-versa. The comparison only succeeds if both are UNKNOWN (returns true) or both are concrete.","triggerScenarios":"Calling resourceSpecA.lessThanOrEqual(resourceSpecB) where one spec was built with explicit CPU/memory values and the other is ResourceSpec.UNKNOWN (or ResourceSpec.DEFAULT, which aliases UNKNOWN). Happens during slot/resource fitting when the scheduler or optimizer compares an operator's ResourceSpec against a provisioned slot's ResourceSpec, and one side was never configured.","commonSituations":"Custom operators or transformations that do not specify resource hints (defaults to UNKNOWN) are compared against explicitly configured slots. Mixing ResourceSpec.ZERO or a builder-constructed spec with default operators in a chain that triggers a lessThanOrEqual check. Resource-aware scheduling misconfiguration where only some operators have resource specs set.","solutions":["Ensure both ResourceSpec operands are concrete (built via the builder with explicit values) or both are UNKNOWN before calling lessThanOrEqual().","Guard the call: if (spec.equals(ResourceSpec.UNKNOWN) || other.equals(ResourceSpec.UNKNOWN)) skip the comparison or handle as a special case.","Replace the UNKNOWN operand with a concrete ResourceSpec.DEFAULT or a builder-constructed spec matching your slot's resources.","When chaining operators, make sure all operators in the chain either have explicit resource specs or all use the default."],"exampleFix":"// before\nResourceSpec a = ResourceSpec.newBuilder().setCpuCores(1.0).setTaskHeapMemoryInMB(100).build();\nboolean fits = a.lessThanOrEqual(ResourceSpec.UNKNOWN); // throws\n\n// after\nResourceSpec slotSpec = ResourceSpec.newBuilder().setCpuCores(2.0).setTaskHeapMemoryInMB(512).build();\nboolean fits = a.lessThanOrEqual(slotSpec); // works: concrete vs concrete","handlingStrategy":"validation","validationCode":"boolean isSafeToCompare(ResourceSpec a, ResourceSpec b) {\n    boolean aUnknown = a.equals(ResourceSpec.UNKNOWN);\n    boolean bUnknown = b.equals(ResourceSpec.UNKNOWN);\n    return (aUnknown && bUnknown) || (!aUnknown && !bUnknown);\n}\n// Before calling a.lessThanOrEqual(b):\nif (!isSafeToCompare(a, b)) {\n    // handle the unknown-vs-concrete case explicitly\n}","typeGuard":"static boolean isConcrete(ResourceSpec spec) {\n    return spec != null && !spec.equals(ResourceSpec.UNKNOWN);\n}","tryCatchPattern":"try {\n    boolean fits = a.lessThanOrEqual(b);\n} catch (IllegalArgumentException e) {\n    // one side is UNKNOWN; handle by treating as unlimited or failing the slot fit\n    log.warn(\"Cannot compare ResourceSpec {} with {}\", a, b);\n}","preventionTips":["Always build ResourceSpec with explicit CPU and memory values via the builder rather than relying on DEFAULT/UNKNOWN.","Centralize ResourceSpec creation in a factory method that always returns concrete specs.","Audit the operator chain to ensure all operators have consistent resource spec strategies."],"tags":["resource-spec","scheduling","unknown-sentinel","flink-core"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}