xai-org/x-algorithm · error · SemanticCheckFailure

ASTNode %s expected %d arguments, %d passed.

Error message

ASTNode %s expected %d arguments, %d passed.

What it means

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.

Source

Thrown at botmaker/src/java/com/twitter/botmaker/ASTNode.java:171

    return scope;
  }

  public static void assertChildConstant(
      String exprText, ImmutableList<ASTNode> children, int index) throws SemanticCheckFailure {
    ASTNode node = children.get(index);
    if (!Constant.class.isAssignableFrom(node.getClass())) {
      throw new SemanticCheckFailure(String.format(
          "type checking expression %s failed: invalid argument type: expected a constant %s",
          exprText,
          node.getReturnType().toString())
      );
    }
  }

  public static void assertChildrenSize(
      String exprText, ImmutableList<ASTNode> children, int expected) throws SemanticCheckFailure {
    if (children.size() != expected) {
      throw new SemanticCheckFailure(String.format(
          "ASTNode %s expected %d arguments, %d passed.", exprText, expected, children.size()));
    }

  }

  public static void assertChildrenSize(
      String exprText, ImmutableList<ASTNode> children,
      int min, int max) throws SemanticCheckFailure {
    if (children.size() < min || children.size() > max) {
      throw new SemanticCheckFailure(String.format(
          "ASTNode %s expected %d to %d arguments, %d passed.",
          exprText, min, max, children.size()));
    }
  }

  public abstract Signature getSignature();

  public abstract Extractor<E> toExtractor();

View on GitHub (pinned to 24c60942c5)

Solutions

  1. Free or reduce other GPU allocations before calling unique (check nvidia-smi for memory headroom)
  2. Reduce input size (chunk the unique operation into batches) so CUB sort scratch fits the allocator budget
  3. Increase the scratch allocator's capacity/budget if it is configured via an environment variable or handle option
  4. Run on a GPU with more free memory or a device with larger total memory
  5. If the allocator is arena-based, ensure the arena is large enough for sort scratch, which can exceed input size for radix sort

Example fix

// before
auto d_temp = scratch_allocator.Allocate(temp_storage_bytes);
if (!d_temp) { throw std::runtime_error("unique: failed to allocate ..."); }

// after
size_t temp_storage_bytes = 0;
XAI_CUDA_CHECK(sort(nullptr, temp_storage_bytes));
// grow arena / free memory first:
scratch_allocator.Reserve(temp_storage_bytes);
auto d_temp = scratch_allocator.Allocate(temp_storage_bytes);
if (!d_temp) { throw std::runtime_error("unique: failed to allocate ..."); }
Defensive patterns

Strategy: fallback

Validate before calling

// Estimate CUB sort scratch before calling unique:
// radix sort temp is roughly 2 * input bytes + counters; be conservative.
size_t estimated = 2 * static_cast<size_t>(n) * sizeof(KeyT) + (1 << 20);
size_t free = 0, total = 0;
cudaMemGetInfo(&free, &total);
if (estimated > free) {
  // chunk the input or free memory first
}

Type guard

bool can_allocate_scratch(ScratchAllocator& alloc, size_t bytes) {
  auto probe = alloc.Allocate(bytes);
  return probe.has_value();  // RAII: probe frees on scope exit if pooled
}

Try / catch

try {
  run_unique(keys, n, scratch_allocator);
} catch (const std::runtime_error& e) {
  if (std::string(e.what()).find("sort scratch") != std::string::npos) {
    // retry with chunked input or after freeing GPU memory
    run_unique_chunked(keys, n, chunk_size, scratch_allocator);
  } else {
    throw;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of xai-org/x-algorithm@24c60942c5 (2026-08-28). Data as JSON: /api/errors/f7348d8e4b7b7359. Report an issue: GitHub.