diff --git a/cpp/EventBaseDataplane.cpp b/cpp/EventBaseDataplane.cpp
--- a/cpp/EventBaseDataplane.cpp
+++ b/cpp/EventBaseDataplane.cpp
@@ -11,11 +11,12 @@
 #include <glog/logging.h>
 
 #include <folly/executors/IOThreadPoolExecutor.h>
+#include <folly/system/HardwareConcurrency.h>
 
 extern "C" folly::IOThreadPoolExecutor*
 common_hs_eventbase_newExecutor() noexcept {
   return new folly::IOThreadPoolExecutor(
-      sysconf(_SC_NPROCESSORS_ONLN),
+      folly::available_concurrency(),
       std::make_shared<folly::NamedThreadFactory>("HaskellIOThreadPool"));
 }
 
diff --git a/cpp/HsOption.h b/cpp/HsOption.h
--- a/cpp/HsOption.h
+++ b/cpp/HsOption.h
@@ -77,7 +77,7 @@
     return *this;
   }
 
-  std::optional<T> toStdOptional()&& {
+  std::optional<T> toStdOptional() && {
     std::optional<T> res = std::move(opt);
     opt = std::nullopt;
     update();
diff --git a/cpp/HsStdTuple.h b/cpp/HsStdTuple.h
--- a/cpp/HsStdTuple.h
+++ b/cpp/HsStdTuple.h
@@ -151,14 +151,14 @@
     return &data_.tup;
   }
 
-  std::tuple<Ts...> toStdTuple()&& {
+  std::tuple<Ts...> toStdTuple() && {
     return std::move(data_.tup);
   }
 
   template <
       typename T = HsStdTuple<Ts...>,
       typename = std::enable_if_t<std::tuple_size_v<T> == 2>>
-  auto toStdPair()&& {
+  auto toStdPair() && {
     return std::make_pair(
         std::move(std::get<0>(data_.tup)), std::move(std::get<1>(data_.tup)));
   }
diff --git a/cpp/HsStruct.h b/cpp/HsStruct.h
--- a/cpp/HsStruct.h
+++ b/cpp/HsStruct.h
@@ -427,7 +427,7 @@
     update();
   }
 
-  std::string toStdString()&& {
+  std::string toStdString() && {
     std::string res(std::move(s_));
     update();
     return res;
@@ -536,7 +536,7 @@
     return v_.size();
   }
 
-  std::vector<T> toStdVector()&& {
+  std::vector<T> toStdVector() && {
     auto res = std::move(v_);
     clear();
     return res;
@@ -666,13 +666,13 @@
     update();
   }
 
-  std::vector<Key> toStdVector()&& {
+  std::vector<Key> toStdVector() && {
     auto res = std::move(k_);
     update();
     return res;
   }
 
-  std::unordered_set<Key> toStdUnorderedSet()&& {
+  std::unordered_set<Key> toStdUnorderedSet() && {
     auto k = std::move(k_);
     std::unordered_set<Key> res(
         std::make_move_iterator(k.begin()), std::make_move_iterator(k.end()));
@@ -806,7 +806,7 @@
     update();
   }
 
-  std::pair<std::vector<Key>, std::vector<Value>> take_items()&& {
+  std::pair<std::vector<Key>, std::vector<Value>> take_items() && {
     auto res = std::make_pair(std::move(k_), std::move(v_));
     update();
     return res;
diff --git a/cpp/HsVariant.h b/cpp/HsVariant.h
--- a/cpp/HsVariant.h
+++ b/cpp/HsVariant.h
@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <common/serialize/SerializeImpl.h>
 #include "cpp/HsStruct.h"
 
 #include <utility>
@@ -15,6 +16,7 @@
 
 struct HsVariant {
   using VariantType = HsJSON;
+  using VariantTypeReference = const VariantType&;
   using MapType = HsObject<HsJSON>;
   using MapKeyType = HsString;
   using MapIterator = MapType::ConstIterator;
@@ -192,6 +194,20 @@
     return it.getValue();
   }
 
+  static bool mapExistsInt64Key(const MapType& map, int64_t key) {
+    auto keyStr = folly::to<std::string>(key);
+    return map.getPtr(folly::StringPiece(keyStr)) != nullptr;
+  }
+
+  static const VariantType& mapAtInt64Key(const MapType& map, int64_t key) {
+    auto keyStr = folly::to<std::string>(key);
+    const auto* ptr = map.getPtr(folly::StringPiece(keyStr));
+    if (!ptr) {
+      throw std::out_of_range("Key not found in map");
+    }
+    return *ptr;
+  }
+
   /** @} */
 
   /**
@@ -308,6 +324,10 @@
     return stringFromData(src, n);
   }
 
+  static StringType createStaticStringSafe(const char* src, int n) {
+    return createStaticString(src, n);
+  }
+
   static StringType getStaticEmptyString() {
     return std::string();
   }
@@ -337,6 +357,13 @@
   static StructHandle registerStruct(
       const StringType& stableIdentifier,
       const std::vector<std::pair<size_t, StringType>>& fields) {
+    return -1;
+  }
+
+  template <typename StableIdentifierCB>
+  static StructHandle registerStruct(
+      StableIdentifierCB&& /*stableIdentifierCb*/,
+      const std::vector<std::pair<size_t, StringType>>& /*fields*/) {
     return -1;
   }
   /** @} */
diff --git a/cpp/RequestContext.h b/cpp/RequestContext.h
--- a/cpp/RequestContext.h
+++ b/cpp/RequestContext.h
@@ -43,6 +43,7 @@
       delete;
   RequestContextPtrScopeGuard& operator=(RequestContextPtrScopeGuard&&) =
       delete;
+  ~RequestContextPtrScopeGuard() = default;
 
  private:
   std::optional<folly::RequestContextScopeGuard> guard_;
diff --git a/cpp/cdynamic.cpp b/cpp/cdynamic.cpp
--- a/cpp/cdynamic.cpp
+++ b/cpp/cdynamic.cpp
@@ -64,8 +64,9 @@
     const dynamic* d,
     size_t /*size*/,
     const dynamic** elems) noexcept {
-  if ((*d).type() != dynamic::ARRAY)
+  if ((*d).type() != dynamic::ARRAY) {
     return 0;
+  }
 
   int i = 0;
   for (const auto& e : *d) {
@@ -84,8 +85,9 @@
     size_t /*size*/,
     const dynamic** keys,
     const dynamic** vals) noexcept {
-  if ((*d).type() != dynamic::OBJECT)
+  if ((*d).type() != dynamic::OBJECT) {
     return 0;
+  }
 
   // Relying on the iterator referring to elements by reference here.
   auto it = (*d).items();
diff --git a/fb-util.cabal b/fb-util.cabal
--- a/fb-util.cabal
+++ b/fb-util.cabal
@@ -3,7 +3,7 @@
 -- Copyright (c) Facebook, Inc. and its affiliates.
 
 name:                fb-util
-version:             0.2.0.0
+version:             0.2.0.1
 synopsis:            Various utility libraries
 homepage:            https://github.com/facebookincubator/hsthrift
 bug-reports:         https://github.com/facebookincubator/hsthrift/issues
@@ -62,11 +62,11 @@
        ghc-options: -O2
 
 common fb-cpp
-  cxx-options: -std=c++17
+  cxx-options: -std=c++20
   -- We use hsc2hs with C++ headers, so we need to compile the
   -- generated code with g++. The hsc2hs-generated binary is linked
   -- by ghc, because we depend on a Haskell package (mangle).
-  hsc2hs-options: --cc=g++ --lflag=-lstdc++ --cflag=-D__HSC2HS__=1 --cflag=-std=c++17
+  hsc2hs-options: --cc=g++ --lflag=-lstdc++ --cflag=-D__HSC2HS__=1 --cflag=-std=c++20
   if !flag(clang)
      cxx-options: -fcoroutines
   if arch(x86_64)
@@ -186,10 +186,11 @@
         HUnit ^>= 1.6.1,
         QuickCheck >= 2.14.3 && < 2.15,
         aeson < 2.3,
-        aeson-pretty >= 0.8.10 && < 0.9,
+        aeson-pretty >= 0.8.9 && < 0.9,
         array ^>=0.5.2.0,
-        async ^>=2.2.1,
-        atomic-primops >= 0.8.8 && < 0.9,
+        async >=2.2.1 && < 2.2.6,
+        -- async-2.2.6 has Control.Concurrent.Stream that conflicts with ours
+        atomic-primops >= 0.8.4 && < 0.9,
         attoparsec >= 0.14.4 && < 0.15,
         attoparsec-aeson >= 2.1 && < 2.3,
         base >=4.11.1.0 && <4.20,
@@ -199,20 +200,20 @@
         clock >= 0.8.4 && < 0.9,
         concurrent-extra >= 0.7.0 && < 0.8,
         containers >=0.5.11 && <0.7,
-        data-default >= 0.8.0 && < 0.9,
+        data-default >= 0.7.1 && < 0.9,
         deepseq >= 1.4.4 && < 1.6,
         directory ^>=1.3.1.5,
         either >= 5.0.2 && < 5.1,
         exceptions >= 0.10.4 && < 0.11,
-        extra >= 1.8 && < 1.9,
+        extra >= 1.7.14 && < 1.9,
         filepath ^>=1.4.2,
         ghc >= 8.6.5 && < 9.9,
         ghci >=8.6.5 && < 9.9,
         hashable >=1.2.7.0 && <1.5,
         haskell-src-exts >= 1.23.1 && < 1.24,
         integer-gmp >=1.0.2.0 && <1.2,
-        json >= 0.11 && < 0.12,
-        lens >= 5.3.3 && < 5.4,
+        json >= 0.10 && < 0.12,
+        lens >= 5.2.3 && < 5.4,
         lifted-base >= 0.2.3 && < 0.3,
         mangle >= 0.1.0 && < 0.2,
         monad-control >= 1.0.3 && < 1.1,
@@ -223,12 +224,12 @@
         primitive < 0.9,
         process ^>=1.6.3.0,
         scientific >= 0.3.7 && < 0.4,
-        some >= 1.0.6 && < 1.1,
+        some >= 1.0.4 && < 1.1,
         split ^>=0.2.3.3,
         stm >= 2.5.0 && < 2.6,
         template-haskell >=2.13 && <2.22,
         text >= 1.2.3.0 && < 2.2,
-        text-show >= 3.10.5 && < 3.12,
+        text-show >= 3.10.4 && < 3.12,
         time >=1.8.0.2 && <1.13,
         transformers >= 0.5.6 && < 0.7,
         unix >= 2.7.2.2 && < 2.9,
@@ -249,7 +250,7 @@
     if flag(folly)
         -- the version here is spliced automatically by
         -- 'make setup-folly-version':
-        build-depends: folly-clib==20250713.1537
+        build-depends: folly-clib==20260203.1245
     else
         build-depends: folly-clib==0.0
 
@@ -331,7 +332,7 @@
   -- We use hsc2hs with C++ headers, so we need to compile the
   -- generated code with g++. The hsc2hs-generated binary is linked
   -- by ghc.
-  hsc2hs-options: --cc=g++ --lflag=-lstdc++ --cflag=-D__HSC2HS__=1 --cflag=-std=c++17
+  hsc2hs-options: --cc=g++ --lflag=-lstdc++ --cflag=-D__HSC2HS__=1 --cflag=-std=c++20
 
 
 test-suite stream
diff --git a/tests/HsStructHelper.cpp b/tests/HsStructHelper.cpp
--- a/tests/HsStructHelper.cpp
+++ b/tests/HsStructHelper.cpp
@@ -41,11 +41,12 @@
 }
 
 void fillCppTuple(hs_std_tuple::CppTupleIntJSONOnlyMovable* t) noexcept {
-  *t = hs_std_tuple::CppTupleIntJSONOnlyMovable(std::make_tuple(
-      42,
-      true,
-      facebook::common::hs::OnlyMovable(8),
-      HsEither<HsString, int64_t>(HsLeft, HsString("wut"s))));
+  *t = hs_std_tuple::CppTupleIntJSONOnlyMovable(
+      std::make_tuple(
+          42,
+          true,
+          facebook::common::hs::OnlyMovable(8),
+          HsEither<HsString, int64_t>(HsLeft, HsString("wut"s))));
 }
 
 } // extern "C"
@@ -69,9 +70,10 @@
     case 2:
       return val->getRight() == 42;
     default:
-      throw std::runtime_error(std::string(
-          "checkHsEither: left_or_right should be either 1 or 2. Given: %d",
-          left_or_right));
+      throw std::runtime_error(
+          std::string(
+              "checkHsEither: left_or_right should be either 1 or 2. Given: %d",
+              left_or_right));
   }
 }
 
diff --git a/tests/HsStructHelper.h b/tests/HsStructHelper.h
--- a/tests/HsStructHelper.h
+++ b/tests/HsStructHelper.h
@@ -32,7 +32,7 @@
   HsString description;
 
  public:
-  Nonmovable(int64_t resource, std::string && description)
+  Nonmovable(int64_t resource, std::string&& description)
       : resource(resource), description(std::move(description)) {}
 
   ~Nonmovable() {
diff --git a/tests/IOBufTest.cpp b/tests/IOBufTest.cpp
--- a/tests/IOBufTest.cpp
+++ b/tests/IOBufTest.cpp
@@ -35,8 +35,9 @@
     bufs[i] = IOBuf::createCombined(50);
     strncpy((char*)bufs[i]->writableData(), strs[i], lens[i]);
     bufs[i]->append(lens[i]);
-    if (i)
+    if (i) {
       bufs[0]->appendChain(std::move(bufs[i]));
+    }
   }
   return bufs[0].release();
 }
