{"record":{"id":"f7348d8e4b7b7359","repo":"xai-org/x-algorithm","slug":"astnode-s-expected-d-arguments-d-passed","errorCode":null,"errorMessage":"ASTNode %s expected %d arguments, %d passed.","messagePattern":"ASTNode (.+?) expected (.+?) arguments, (.+?) passed\\.","errorType":"validation","errorClass":"SemanticCheckFailure","httpStatus":null,"severity":"error","filePath":"botmaker/src/java/com/twitter/botmaker/ASTNode.java","lineNumber":171,"sourceCode":"    return scope;\n  }\n\n  public static void assertChildConstant(\n      String exprText, ImmutableList<ASTNode> children, int index) throws SemanticCheckFailure {\n    ASTNode node = children.get(index);\n    if (!Constant.class.isAssignableFrom(node.getClass())) {\n      throw new SemanticCheckFailure(String.format(\n          \"type checking expression %s failed: invalid argument type: expected a constant %s\",\n          exprText,\n          node.getReturnType().toString())\n      );\n    }\n  }\n\n  public static void assertChildrenSize(\n      String exprText, ImmutableList<ASTNode> children, int expected) throws SemanticCheckFailure {\n    if (children.size() != expected) {\n      throw new SemanticCheckFailure(String.format(\n          \"ASTNode %s expected %d arguments, %d passed.\", exprText, expected, children.size()));\n    }\n\n  }\n\n  public static void assertChildrenSize(\n      String exprText, ImmutableList<ASTNode> children,\n      int min, int max) throws SemanticCheckFailure {\n    if (children.size() < min || children.size() > max) {\n      throw new SemanticCheckFailure(String.format(\n          \"ASTNode %s expected %d to %d arguments, %d passed.\",\n          exprText, min, max, children.size()));\n    }\n  }\n\n  public abstract Signature getSignature();\n\n  public abstract Extractor<E> toExtractor();","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/xai-org/x-algorithm/blob/24c60942c5c5fdad3a6addffb4c6e6d2f228f04f/botmaker/src/java/com/twitter/botmaker/ASTNode.java#L153-L189","documentation":"The unique (deduplication) kernel uses CUB's DeviceRadixSort (or DeviceSegmentedRadixSort) which requires temporary scratch memory sized by a dry-run query. The code queries temp_storage_bytes, then asks its scratch allocator for that many bytes; if the allocator returns an empty optional it throws this error, aborting the unique operation before any sorting runs.","triggerScenarios":"Calling the unique op on a very large input so the CUB sort needs more scratch than the allocator's budget/cap allows; a scratch allocator backed by a pre-allocated arena or memory pool that is exhausted; a CUDA context where cudaMalloc under the hood fails (other allocations consuming GPU memory); or a zero/oversized temp_storage_bytes path interacting badly with allocator limits.","commonSituations":"Large ragged tensors causing huge sort scratch requirements, memory fragmentation in a long-running process, running multiple CUDA ops concurrently that share a fixed scratch pool, or GPU memory pressure from other jobs on the same device.","solutions":["Free or reduce other GPU allocations before calling unique (check nvidia-smi for memory headroom)","Reduce input size (chunk the unique operation into batches) so CUB sort scratch fits the allocator budget","Increase the scratch allocator's capacity/budget if it is configured via an environment variable or handle option","Run on a GPU with more free memory or a device with larger total memory","If the allocator is arena-based, ensure the arena is large enough for sort scratch, which can exceed input size for radix sort"],"exampleFix":"// before\nauto d_temp = scratch_allocator.Allocate(temp_storage_bytes);\nif (!d_temp) { throw std::runtime_error(\"unique: failed to allocate ...\"); }\n\n// after\nsize_t temp_storage_bytes = 0;\nXAI_CUDA_CHECK(sort(nullptr, temp_storage_bytes));\n// grow arena / free memory first:\nscratch_allocator.Reserve(temp_storage_bytes);\nauto d_temp = scratch_allocator.Allocate(temp_storage_bytes);\nif (!d_temp) { throw std::runtime_error(\"unique: failed to allocate ...\"); }","handlingStrategy":"fallback","validationCode":"// Estimate CUB sort scratch before calling unique:\n// radix sort temp is roughly 2 * input bytes + counters; be conservative.\nsize_t estimated = 2 * static_cast<size_t>(n) * sizeof(KeyT) + (1 << 20);\nsize_t free = 0, total = 0;\ncudaMemGetInfo(&free, &total);\nif (estimated > free) {\n  // chunk the input or free memory first\n}","typeGuard":"bool can_allocate_scratch(ScratchAllocator& alloc, size_t bytes) {\n  auto probe = alloc.Allocate(bytes);\n  return probe.has_value();  // RAII: probe frees on scope exit if pooled\n}","tryCatchPattern":"try {\n  run_unique(keys, n, scratch_allocator);\n} catch (const std::runtime_error& e) {\n  if (std::string(e.what()).find(\"sort scratch\") != std::string::npos) {\n    // retry with chunked input or after freeing GPU memory\n    run_unique_chunked(keys, n, chunk_size, scratch_allocator);\n  } else {\n    throw;\n  }\n}","preventionTips":["Monitor free GPU memory (cudaMemGetInfo) before sort-heavy unique ops","Size scratch arenas for worst-case input, not average","Chunk large inputs so per-chunk temporaries stay small","Avoid running multiple scratch-consuming ops concurrently on one allocator"],"tags":["cuda","cub","out-of-memory","sort","scratch-memory","unique"],"backgroundTag":"cuda-out-of-memory","analyzedSha":"24c60942c5c5fdad3a6addffb4c6e6d2f228f04f","analyzedAt":"2026-08-28T11:40:14.686Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}