xai-org/x-algorithm · error · SemanticCheckFailure

ASTNode expected arguments, 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.

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.

Appendix: 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)