Repository: single-cell-data/TileDB-SOMA (language: python)

Issue:
[python/r/c++] Revisit `shape` for component arrays
## PRs

**Merged PRs:**

<details>

* #2909 `kerl/schevo-timestamp-methodize`
* #2913 `kerl/name-neaten`
* #2908 `kerl/ut-soma-exc-simplify`
* #2910 `kerl/test-common-parameterize`
* #2918 `kerl/cpp-test-deadstrip`
* #2919 `kerl/minor-unit-test-helper-mod`
* #2936 `kerl/cpp-ut-helper-neaten`
* #2938 `kerl/more-cur-dom-parameterize`
* #2915 `kerl/cpp-strict-int64-shape`
* #2911 `kerl/arrow-util-current-domain-optional`
* #2939 `kerl/step-two-temp`
* #2947 `kerl/cpp-ndarray-resize-testing`
* #2945 `kerl/dataframe-test-fixture`
* #2944 `kerl/cpp-variant-indexed-dataframes`
* #2916 `kerl/sdf-shape`
* #2917 `kerl/cpp-resizes`
* #2948 `kerl/upgrade-shape-int64`
* #2951 `kerl/sdf-test-accessors`
* #2953 `kerl/py-r-accessor-plumbing`
* #2957 `kerl/sdf-domain-accessors`
* #2960 `kerl/dense-link`
* #2963 `kerl/nightly-fix`
* #2970 `kerl/dense-writeable-after-create`
* #2968 `kerl/minor-trim`
* #2969 `kerl/more-py-domain-name-neaten`
* #2972 `kerl/libtiledbsoma-env-logging-level`
* #2962 `kerl/py-r-creation-paths`
* #2950 `kerl/py-r-test-2`
* #2994 `kerl/nanoarrow-helpers`
* #3011 `kerl/polydom3`
* #3017 `kerl/polydom5`
* #3018 `kerl/polydom6`
* #2990 `kerl/variant-nnz-bug`
* #3019 `kerl/index-swap`
* #3020 `kerl/ut-max-shape`
* #3012 `kerl/polydom4`
* #3025 `kerl/fix-3020-merge`
* #3026 `kerl/one-more-rename`
* #3028 `kerl/ff-not`
* #3029 `kerl/ut-vg`
* #3030 `kerl/table-utils-memory`
* #3067 `kerl/improve-sdf-test-field-names`
* #3068 `kerl/ut-generate`
* #3069 `kerl/cpp-sdf-domain-at-create`
* #3027 `kerl/hll-domainish`
* #3088 `kerl/max-domain-int64`
* #3090 `kerl/maybe-resize-soma-joinid-cpp-tweak`
* #3032 `kerl/sdf-domain-at-create` -- fixes #2967
* #3091 `kerl/maybe-resize-soma-joinid-py-r`
* #3095 `kerl/cpp-exp-resize-prep`
* #3089 `kerl/r-dataframe-shapeable`
* https://github.com/single-cell-data/SOMA/pull/233
* #3125 `kerl/cpp-ut-name-shortens`
* #3127 `kerl/helper-rename`
* #3130 `kerl/cpp-can-resizers-names`
* #3132 `kerl/cpp-dataframe-sizing-helpers`
* #3139 `kerl/cpp-dataframe-upgrade-test`
* #3140 `kerl/py-resizer-connects`
* #3151 `kerl/py-can-upgrade-shape`
* #3152 `kerl/registration-shape-acceessors`
* #3156 `kerl/py-exp-shaping`
* #3157 `kerl/py-exp-shaping2`
* #3148 `kerl/py-exp-resize`
* #3191 `kerl/py-domain-at-create-ut-1`
* #3190 `kerl/py-domain-at-create-ut-2`
* #3192 `kerl/py-domain-at-create-ut-3`
* #3193 `kerl/py-domain-at-create-ut-4`
* #3194 `kerl/py-domain-at-create-ut-5`
* #3203 `kerl/min-size-2`
* #3208 `kerl/r-min-sizing`
* #3211 `kerl/cpp-ugr-dom`
* #3232 `kerl/ff-interop`
* #3230 `kerl/ffon`
* #3234 `kerl/docstring-prune`
* #3236 `kerl/prefixing`
* #3241 `kerl/fix-bad-merge`
* #3235 `kerl/py-r-ugr-dom`
* #3237 `kerl/py-r-ugr-dom-2`
* #3238 `kerl/py-r-ugr-dom-3`
* #3253 `kerl/set-coords-rename`
* #3261 `kerl/pybind11-nda-sizing`
* #3265 `kerl/dense-227-a`
* #3263 `kerl/dense-range-trim`
* #3268 `kerl/dim-explosion`
* #3269 `kerl/python-227-dense-ned-read`
* #3270 `kerl/r-227-dense-fixes`
* #3280 `kerl/r-dense-227-more`
* #3286 `kerl/more-fn4m`
* #3283 `kerl/readthedocs-pre-1.15`
* #3288 `kerl/dense-ugrsh`
* #3289 `kerl/notebook-shape-upgrade`
* #3285 `kerl/new-shape-doc-updates`
* #3295 `kerl/notebook-data-refresh`
* #3290 `kerl/notebook-new-shape-refresh`
* #3301 `kerl/ffena`
* #3303 `kerl/r-data-refresh`
* #3300 `kerl/sdf-sjid-lower-zero`
* #3296 `kerl/dense-example-data-refresh`
* #3294 `kerl/new-shape-notebook-and-vignette`
* #3308 `kerl/upgrade-experiment-resources`
* #3309 `kerl/fix-notebook-merge`
* #3321 `kerl/more-use-shape`
* #3358 `kerl/revert-3300`
* #3368 `kerl/227a`
* #3369 `kerl/ucd1`
* #3370 `kerl/ucd2`
* #3371 `kerl/ucd3`
* #3372 `kerl/ucd4`
* #3396 `kerl/domain-at-create-docstrings`
* #3302 `kerl/new-shape-vignette`
* https://github.com/single-cell-data/SOMA/pull/250
* #3399 `kerl/new-shape-more-docstrings`
* #3400 `kerl/check-only-r`
* https://github.com/TileDB-Inc/somacore-feedstock/pull/27

</details>

**Closed/abandoned PRs:**

<details>

* #2785 -- This was only dogfooding for the core 2.25 release -- not to be merged
* #2952 `kerl/feature-flag-temp` -- folded into 2962
* #2995 `kerl/polydom`
* #2964 `kerl/tiledbsoma-io-test`
* #3189 `kerl/min-size`
* #3220 `kerl/cpp-ugr-dom-2`
* #3244 `kerl/dense-227-fixes`
* #3276 `kerl/dataframe-shape`

</details>

**Issues which are related but non-blocking:**

* https://github.com/single-cell-data/SOMA/pull/216
* https://github.com/TileDB-Inc/TileDB/pull/5303
* #2966
* #3081
* Note: R append mode does not exist yet -- see #1630 -- so an experiment-level resizer is not a priority in R
* #3271 
* #3272
* #3273

**See also:** [[sc-51048]](https://app.shortcut.com/tiledb-inc/story/51048).

## Problem to be solved

Users want to know the `shape` of an array, in the SciPy sense:

* Reads and writes are bounds-checked against the shape
* This retains its value regardless of which values of a sparse array are or are not actually occupied
* Users can `resize`.
  * Some users need the ability to grow their datasets later, using either `tiledbsoma.io`'s append mode, or subsequent writes using the `tiledbsoma` API.
  * Note that the cellxgene census doesn't need this: eact week's published census has fixed shape, and any updates will happen in new storage, on a new week.

Using TileDB-SOMA up until the present:

* The TIleDB `domain` is immutable after array creation
  * This does bounds-checking for reads and writes, which is good
  * To leverage this to function as a `shape`, users would need to set the `domain` at array-creation time. However, users lose the ability to grow their datasets later.
* There is a `non_empty_domain` accessor
  * This only indicates min/max coordinates _at which data exists_. Consider an `X` array for 100 cells and 200 genes. If non-zero expression counts exist only for cell join IDs 2-17, then the `non_empty_domain` will indicate `(2,17)` along `soma_dim_0`.
  * Consider an `obms["X_pca"]` within the same experiment. This may be 100 cells by 50 PCA components: we need a placd to store the number 50.
  * Therefore users cannot leverage this to function as a `shape` accessor.
* We have offered a `used_shape` accessor since TileDB-SOMA 1.5.
  * This functions as a `shape` accessor, in the SciPy sense, but it is not multi-writer safe.

New feature for TileDB-SOMA 1.15:

* Arrays will have a  `shape`
* Reads and writes are bounds-checked against the shape
* This retains its value regardless of which values of a sparse array are or are not actually occupied
* Users can `resize`
* The `used_shape` accessor will be deprecated in TileDB-SOMA 1.13, and slated for removal in TileDB-SOMA 1.14.

Compatiblity:

This will now require users to do an explicit `resize` before appending/growing TileDB-SOMA Experiments. Guidance in the form of example notebooks will be provided.

##  Tracking

See also: [[sc-41074]](https://app.shortcut.com/tiledb-inc/story/41074) and [[sc-51048]](https://app.shortcut.com/tiledb-inc/story/51048).

## Scheduling

Support arrives in TileDB Core 2.25. Deprecations for TileDB-SOMA will be released with 1.13. Full support within TileDB-SOMA will be release in 1.14.

## Details

SOMA API mods as we've discussed in a Google doc are as follows.

### `SOMADataFrame`

* `create`: Retain the `domain` argument
  * Issue:
    * Core has a `(lo, hi)` tuple per dim, e.g. `(0,99)` or `(10,19)`
    * SOMA has count per dim, with 0 implicit: e.g. 100 or 20
    * For `SparseNDArray` and `DenseNDArray` core can have `(lo, hi)` and SOMA can have `count`
    * For `DataFrame` there can be multiple dims --- default is a single `soma_joinid`
    * That could be treated either in `(lo, hi)` fashion or `count` fashion
    * However additional dims (e.g. `cell_type`) can be on any type, including strings, floats, etc. where there is no implicit lo=0
    * Therefore we need to keep the current SOMA API wherein `DataFrame` takes a `domain` argument (in `(lo, hi)` fashion) and not a `shape` argument (in `count` fashion)

### `SparseNDArray and DenseNDArray`

* `create`
  * Have an optional shape argument which is of type `Tuple[Int,...]` where each element is the cell count of the corresponding dimension
    * If unsupplied, or if supplied but None in any slot: use the minimum 0 in each slot – nothing larger makes sense since we will not support downsize
  * User guidance should make clear that it will not be possible to create an ‘old’ style array with the ‘new style’ API. (See also the upgrade logic below.)

### All three of `SOMADataFrame`, `SparseNDArray`, `DenseNDArray`

* `write`
  * For new arrays, created with the new shape feature:
    * Core will bounds-check that coordinates provided at `write` time are within the current shape
    * Core will raise `tiledb.cc.TileDBError` to TileDB-SOMA, which will catch and raise `IndexError`, and R-standard behavior on the R side
  * For old arrays created before this feature:
    * Core will not bounds-check that coordinates provided at write time are within the current shape
* Existing `used_shape` accessor
  * TileDB-SOMA will deprecate this over a release cycle.
  * For new arrays: raise `NotImplementedError`
  * For old arrays: return what’s currently returned, with a deprecation warning.
  * Mechanism for determining old vs. new: `array.schema.version` (the core storage version).
* Existing `shape` accessor
  * For new arrays:
    * Have this return the new shape as proposed by core, no longer returning the TileDB domain.
  * For old arrays created before this feature:
    * Return the TileDB domain as now.
* Existing `non_empty_domain` accessor
  * Same behavior for old and new arrays (unaffected by this proposal).
  * Keep this accessor supported, but, with user notes that it’s generally non-useful
  * This should return None (or R equivalent) when there is a schema but no data have been written.
* New `maxshape` accessor
  * Maps the core-level `(lo, hi)` accessor for domain to count-style accessor hi+1. E.g. if the core domain is either `(0,99)` or `(50,99)` then TileDB-SOMA `maxshape` will say 100.
  * Same behavior for old and new arrays.
  * Let users query for what the TileDB domain is, with user notes that it’s the maximum that users can reshape to.
  * Issac suggests: maybe `domain` or `maxshape` (see h5py).
* New `resize` mutator
  * Note: `reshape` means something else in the community (numpy, zarr, h5py), e.g. a 5x20 (total 100 cells) being reinterpreted as 4x25 (still 100 cells). The standard name for changing cell-count is `resize`.
  * For old arrays created before this feature: raise `NotImplementedError`.
  * For new arrays:
    * Will raise `ValueError` if the new shape is smaller on any dim than currently in storage
    * Regardless of whether any data have been written whatsoever
    * Will raise `ValueError` if the new shape exceeds the TileDB domain from create time, which will serve TileDB-SOMA in a role of “max possible shape the user can reshape to”
    * Otherwise, any calls to write from this point will bounds-check writes within this new shape
    * **We don’t expect resize to be multi-writer safe with regard to write ; user notes must be clear on this point**
* New `tiledbsoma_upgrade_shape` method for SparseNDArray and DenseNDArray
  * This will leverage `array.schema.version` to see if an upgrade is needed
  * Leverage core support for storage-version updates
  * This will take a shape argument as in `create`
  * For arrays created with “just-right” size: this will succeed
  * For arrays created with “room-for-growth” / “two billion-ish” size: this will succeed
  * If the user passes a shape which exceeds the current TileDB domain: this will fail
* New `tiledbsoma_upgrade_domain method` for `DataFrame`
  * Same as for `SparseNDArray`/`DenseNDArray` except it will take a domain at the SOMA-API level just as `DataFrame`'s create method

### `tiledbsoma.io`

* The user-facing API has no shape arguments and thus won’t need changing.
* Internally to `tiledbsoma.io`, we’ll still ask the tiledbsoma API for the “big domain” (2 billionish)
* Append mode:
  * Will need a new `resize` method at the `Experiment` level
  * Users will need to:
    * Register as now
    * Call the experiment-level `resize`
      * Could be `exp.resize(...)`, or (better) this could be `tiledbsoma.io.reshape_experiment`
  * In either case: this method will take the new `obs` and `var` counts as inputs:
    * `exp.obs.reshape` to new `obs` count
    * `exp.ms[name].var.reshape` to new `var` count
    * `exp.ms[name].X[name].reshape` to new `obs` count x `var` count
    * `exp.ms[name].obsm[name].reshape` to new `obs` count x same width
    * `exp.ms[name].obsp[name].reshape` to new `obs` count x `obs` count
    * `exp.ms[name].varm[name].reshape` to new `var` count x same width
    * `exp.ms[name].varp[name].reshape` to new `var` count x `var` count
  * Do the individual append-mode writes as now

[python] New-shape testing for `tiledbsoma.io` [WIP]
**Issue and/or context:** As tracked on issue #2407 / [[sc-51048]](https://app.shortcut.com/tiledb-inc/story/51048). Atop #2950.

Note that the intended Python and R API changes are all agreed on and finalized as described in #2407.

**Changes:**

Tracks the things needing changed for `tiledbsoma.io` wherein explicit resize will play a key role.

**Notes for Reviewer:**

This PR is a work in progress. It is not ready for review.


[c++] Trivial name-shortens in unit-test code
**Issue and/or context:** In support of issue #2407 / [[sc-51048]](https://app.shortcut.com/tiledb-inc/story/51048).

**Changes:**

1st commit: I prefer longer/clearer names often, but, these are repeated and are causing unnecessary `clang-format` adjustments, particularly in PRs stacked on top of this.

2nd commit: another renaming. Not worth a separate PR IMO.

**Notes for Reviewer:**
[c++] Propagate Python/R function names to C++ for upgrade/resize methods
**Issue and/or context:** As tracked on issue #2407 / [[sc-51048]](https://app.shortcut.com/tiledb-inc/story/51048).

**Changes:**

* This is underpinning for #2964
* As @nguyenv pointed out, it might be nice for the C++ layer to just return enums only ...
* ... but as I pointed out
  * informative messages really require specific context such as the user-provided shape and the limit it ran into
  * I don't want to write those all twice in Python and R
  * So we are making user-presentable strings in the C++ code
* That said, we should not be hard-coding the function names from the caller -- given that C++ is creating user-facing error strings _on behalf of_ Python and R, the C++ code should enable and empower the Python/R code to pass in their function names (whatever those are)

**Notes for Reviewer:**

Despite the line-count this is a trivial PR. I'm splitting out this PR (as well as recents #3125 #3127 #3130) in order to clear the picture for #2964 which is the contentful goal of my recent work this week.


Discussion / hints:
#2785 is a quick-and-dirty concept-prover -- its sole function is to flush out any API misunderstandings we might have, in prep for 2.25.0 core release.




Fix patch:
diff --git a/apis/python/src/tiledbsoma/soma_dataframe.cc b/apis/python/src/tiledbsoma/soma_dataframe.cc
index 97909558c5..43bb11c444 100644
--- a/apis/python/src/tiledbsoma/soma_dataframe.cc
+++ b/apis/python/src/tiledbsoma/soma_dataframe.cc
@@ -161,7 +161,8 @@ void load_soma_dataframe(py::module& m) {
             "resize_soma_joinid_shape",
             [](SOMADataFrame& sdf, int64_t newshape) {
                 try {
-                    sdf.resize_soma_joinid_shape(newshape);
+                    sdf.resize_soma_joinid_shape(
+                        newshape, "resize_soma_joinid_shape");
                 } catch (const std::exception& e) {
                     throw TileDBSOMAError(e.what());
                 }
diff --git a/apis/python/src/tiledbsoma/soma_sparse_ndarray.cc b/apis/python/src/tiledbsoma/soma_sparse_ndarray.cc
index 3b38860ba6..5bfbf27b3f 100644
--- a/apis/python/src/tiledbsoma/soma_sparse_ndarray.cc
+++ b/apis/python/src/tiledbsoma/soma_sparse_ndarray.cc
@@ -121,7 +121,7 @@ void load_soma_sparse_ndarray(py::module& m) {
             "resize",
             [](SOMAArray& array, const std::vector<int64_t>& newshape) {
                 try {
-                    array.resize(newshape);
+                    array.resize(newshape, "resize");
                 } catch (const std::exception& e) {
                     throw TileDBSOMAError(e.what());
                 }
@@ -132,7 +132,7 @@ void load_soma_sparse_ndarray(py::module& m) {
             "tiledbsoma_upgrade_shape",
             [](SOMAArray& array, const std::vector<int64_t>& newshape) {
                 try {
-                    array.upgrade_shape(newshape);
+                    array.upgrade_shape(newshape, "tiledbsoma_upgrade_shape");
                 } catch (const std::exception& e) {
                     throw TileDBSOMAError(e.what());
                 }
diff --git a/apis/r/R/RcppExports.R b/apis/r/R/RcppExports.R
index 71725d12d8..961f69b01e 100644
--- a/apis/r/R/RcppExports.R
+++ b/apis/r/R/RcppExports.R
@@ -227,7 +227,7 @@ resize <- function(uri, new_shape, ctxxp) {
 }
 
 resize_soma_joinid_shape <- function(uri, new_shape, ctxxp) {
-    invisible(.Call(`_tiledbsoma_resize_soma_joinid`, uri, new_shape, ctxxp))
+    invisible(.Call(`_tiledbsoma_resize_soma_joinid_shape`, uri, new_shape, ctxxp))
 }
 
 tiledbsoma_upgrade_shape <- function(uri, new_shape, ctxxp) {
diff --git a/apis/r/src/RcppExports.cpp b/apis/r/src/RcppExports.cpp
index 3cf04fd4a4..b530b175c6 100644
--- a/apis/r/src/RcppExports.cpp
+++ b/apis/r/src/RcppExports.cpp
@@ -514,7 +514,7 @@ END_RCPP
 }
 // resize_soma_joinid_shape
 void resize_soma_joinid_shape(const std::string& uri, Rcpp::NumericVector new_shape, Rcpp::XPtr<somactx_wrap_t> ctxxp);
-RcppExport SEXP _tiledbsoma_resize_soma_joinid(SEXP uriSEXP, SEXP new_shapeSEXP, SEXP ctxxpSEXP) {
+RcppExport SEXP _tiledbsoma_resize_soma_joinid_shape(SEXP uriSEXP, SEXP new_shapeSEXP, SEXP ctxxpSEXP) {
 BEGIN_RCPP
     Rcpp::RNGScope rcpp_rngScope_gen;
     Rcpp::traits::input_parameter< const std::string& >::type uri(uriSEXP);
@@ -761,7 +761,7 @@ static const R_CallMethodDef CallEntries[] = {
     {"_tiledbsoma_c_attrnames", (DL_FUNC) &_tiledbsoma_c_attrnames, 2},
     {"_tiledbsoma_c_schema", (DL_FUNC) &_tiledbsoma_c_schema, 2},
     {"_tiledbsoma_resize", (DL_FUNC) &_tiledbsoma_resize, 3},
-    {"_tiledbsoma_resize_soma_joinid", (DL_FUNC) &_tiledbsoma_resize_soma_joinid, 3},
+    {"_tiledbsoma_resize_soma_joinid_shape", (DL_FUNC) &_tiledbsoma_resize_soma_joinid_shape, 3},
     {"_tiledbsoma_tiledbsoma_upgrade_shape", (DL_FUNC) &_tiledbsoma_tiledbsoma_upgrade_shape, 3},
     {"_tiledbsoma_c_update_dataframe_schema", (DL_FUNC) &_tiledbsoma_c_update_dataframe_schema, 6},
     {"_tiledbsoma_sr_setup", (DL_FUNC) &_tiledbsoma_sr_setup, 10},
diff --git a/apis/r/src/rinterface.cpp b/apis/r/src/rinterface.cpp
index 4400cb60d4..a6a3c02b0d 100644
--- a/apis/r/src/rinterface.cpp
+++ b/apis/r/src/rinterface.cpp
@@ -428,7 +428,7 @@ void resize(
     // https://github.com/single-cell-data/TileDB-SOMA/issues/2407.
     auto sr = tdbs::SOMAArray::open(OpenMode::write, uri, ctxxp->ctxptr);
     std::vector<int64_t> new_shape_i64 = i64_from_rcpp_numeric(new_shape);
-    sr->resize(new_shape_i64);
+    sr->resize(new_shape_i64, "resize");
     sr->close();
 }
 
@@ -440,7 +440,7 @@ void resize_soma_joinid_shape(
     // This function is solely for SOMADataFrame.
     auto sr = tdbs::SOMADataFrame::open(uri, OpenMode::write, ctxxp->ctxptr);
     std::vector<int64_t> new_shape_i64 = i64_from_rcpp_numeric(new_shape);
-    sr->resize_soma_joinid_shape(new_shape_i64[0]);
+    sr->resize_soma_joinid_shape(new_shape_i64[0], "resize_soma_joinid_shape");
     sr->close();
 }
 
@@ -455,7 +455,7 @@ void tiledbsoma_upgrade_shape(
     // https://github.com/single-cell-data/TileDB-SOMA/issues/2407.
     auto sr = tdbs::SOMAArray::open(OpenMode::write, uri, ctxxp->ctxptr);
     std::vector<int64_t> new_shape_i64 = i64_from_rcpp_numeric(new_shape);
-    sr->upgrade_shape(new_shape_i64);
+    sr->upgrade_shape(new_shape_i64, "tiledbsoma_upgrade_shape");
     sr->close();
 }
 
diff --git a/libtiledbsoma/src/soma/soma_array.cc b/libtiledbsoma/src/soma/soma_array.cc
index 2932327e1d..394427df5a 100644
--- a/libtiledbsoma/src/soma/soma_array.cc
+++ b/libtiledbsoma/src/soma/soma_array.cc
@@ -524,8 +524,8 @@ void SOMAArray::_promote_indexes_to_values(
             return _cast_dictionary_values<double>(schema, array);
         default:
             throw TileDBSOMAError(fmt::format(
-                "Saw invalid TileDB value type when attempting to "
-                "promote indexes to values: {}",
+                "Saw invalid TileDB value type when attempting to promote "
+                "indexes to values: {}",
                 tiledb::impl::type_to_str(value_type)));
     }
 }
@@ -1452,7 +1452,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_helper(
         return std::pair(
             false,
             fmt::format(
-                "cannot {}: provided shape has ndim {}, while the array has {}",
+                "{}: provided shape has ndim {}, while the array has {}",
                 function_name_for_messages,
                 arg_ndim,
                 array_ndim));
@@ -1481,8 +1481,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_helper(
                 false,
                 fmt::format(
                     "{}: array already has a shape: please use resize rather "
-                    "than "
-                    "tiledbsoma_upgrade_shape.",
+                    "than tiledbsoma_upgrade_shape.",
                     function_name_for_messages));
         }
     }
@@ -1497,7 +1496,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_helper(
     //
     // if the requested shape fits in the array's core domain, it's good to go
     // as a new shape.
-    auto domain_check = _can_set_shape_domainish_helper(
+    auto domain_check = _can_set_shape_domainish_subhelper(
         newshape, false, function_name_for_messages);
     if (!domain_check.first) {
         return domain_check;
@@ -1506,7 +1505,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_helper(
     // For new-style arrays, we need to additionally that the the requested
     // shape (core current domain) isn't a downsize of the current one.
     if (has_shape) {
-        auto current_domain_check = _can_set_shape_domainish_helper(
+        auto current_domain_check = _can_set_shape_domainish_subhelper(
             newshape, true, function_name_for_messages);
         if (!current_domain_check.first) {
             return current_domain_check;
@@ -1519,7 +1518,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_helper(
 // This is a helper for _can_set_shape_helper: it's used for comparing
 // the user's requested shape against the core current domain or core (max)
 // domain.
-std::pair<bool, std::string> SOMAArray::_can_set_shape_domainish_helper(
+std::pair<bool, std::string> SOMAArray::_can_set_shape_domainish_subhelper(
     const std::vector<int64_t>& newshape,
     bool check_current_domain,
     std::string function_name_for_messages) {
@@ -1536,8 +1535,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_domainish_helper(
         // library-internal code, it's not the user's fault if we got here.
         if (dim.type() != TILEDB_INT64) {
             throw TileDBSOMAError(fmt::format(
-                "{}: internal error: expected {} dim to "
-                "be {}; got {}",
+                "{}: internal error: expected {} dim to be {}; got {}",
                 function_name_for_messages,
                 dim_name,
                 tiledb::impl::type_to_str(TILEDB_INT64),
@@ -1553,7 +1551,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_domainish_helper(
                 return std::pair(
                     false,
                     fmt::format(
-                        "cannot {} for {}: new {} < existing shape {}",
+                        "{} for {}: new {} < existing shape {}",
                         function_name_for_messages,
                         dim_name,
                         newshape[i],
@@ -1569,7 +1567,7 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_domainish_helper(
                 return std::pair(
                     false,
                     fmt::format(
-                        "cannot {} for {}: new {} < maxshape {}",
+                        "{} for {}: new {} < maxshape {}",
                         function_name_for_messages,
                         dim_name,
                         newshape[i],
@@ -1580,15 +1578,17 @@ std::pair<bool, std::string> SOMAArray::_can_set_shape_domainish_helper(
     return std::pair(true, "");
 }
 
-std::pair<bool, std::string> SOMAArray::can_resize_soma_joinid(
-    int64_t newshape) {
+std::pair<bool, std::string> SOMAArray::can_resize_soma_joinid_shape(
+    int64_t newshape, std::string function_name_for_messages) {
     // Fail if the array doesn't already have a shape yet (they should upgrade
     // first).
     if (!has_current_domain()) {
         return std::pair(
             false,
-            "can_resize_soma_joinid: dataframe currently has no domain set: "
-            "please use tiledbsoma_upgrade_domain.");
+            fmt::format(
+                "{}: dataframe currently has no domain set: please "
+                "upgrade the array.",
+                function_name_for_messages));
     }
 
     // OK if soma_joinid isn't a dim.
@@ -1602,8 +1602,8 @@ std::pair<bool, std::string> SOMAArray::can_resize_soma_joinid(
         return std::pair(
             false,
             fmt::format(
-                "cannot resize_soma_joinid_shape: new soma_joinid shape {} < "
-                "existing shape {}",
+                "{}: new soma_joinid shape {} < existing shape {}",
+                function_name_for_messages,
                 newshape,
                 cur_dom_lo_hi.second));
     }
@@ -1614,8 +1614,8 @@ std::pair<bool, std::string> SOMAArray::can_resize_soma_joinid(
         return std::pair(
             false,
             fmt::format(
-                "cannot resize_soma_joinid_shape: new soma_joinid shape {} > "
-                "maxshape {}",
+                "{}: new soma_joinid shape {} > maxshape {}",
+                function_name_for_messages,
                 newshape,
                 dom_lo_hi.second));
     }
@@ -1624,27 +1624,34 @@ std::pair<bool, std::string> SOMAArray::can_resize_soma_joinid(
     return std::pair(true, "");
 }
 
-void SOMAArray::resize(const std::vector<int64_t>& newshape) {
+void SOMAArray::resize(
+    const std::vector<int64_t>& newshape,
+    std::string function_name_for_messages) {
     if (_get_current_domain().is_empty()) {
-        throw TileDBSOMAError(
-            "[SOMAArray::resize] array must already have a shape");
+        throw TileDBSOMAError(fmt::format(
+            "{} array must already have a shape", function_name_for_messages));
     }
-    _set_current_domain_from_shape(newshape);
+    _set_current_domain_from_shape(newshape, function_name_for_messages);
 }
 
-void SOMAArray::upgrade_shape(const std::vector<int64_t>& newshape) {
+void SOMAArray::upgrade_shape(
+    const std::vector<int64_t>& newshape,
+    std::string function_name_for_messages) {
     if (!_get_current_domain().is_empty()) {
-        throw TileDBSOMAError(
-            "[SOMAArray::resize] array must not already have a shape");
+        throw TileDBSOMAError(fmt::format(
+            "{}: array must not already have a shape",
+            function_name_for_messages));
     }
-    _set_current_domain_from_shape(newshape);
+    _set_current_domain_from_shape(newshape, function_name_for_messages);
 }
 
 void SOMAArray::_set_current_domain_from_shape(
-    const std::vector<int64_t>& newshape) {
+    const std::vector<int64_t>& newshape,
+    std::string function_name_for_messages) {
     if (mq_->query_type() != TILEDB_WRITE) {
-        throw TileDBSOMAError(
-            "[SOMAArray::resize] array must be opened in write mode");
+        throw TileDBSOMAError(fmt::format(
+            "{} array must be opened in write mode",
+            function_name_for_messages));
     }
 
     // Variant-indexed dataframes must use a separate path
@@ -1677,10 +1684,12 @@ void SOMAArray::_set_current_domain_from_shape(
     schema_evolution.array_evolve(uri_);
 }
 
-void SOMAArray::resize_soma_joinid_shape(int64_t newshape) {
+void SOMAArray::resize_soma_joinid_shape(
+    int64_t newshape, std::string function_name_for_messages) {
     if (mq_->query_type() != TILEDB_WRITE) {
-        throw TileDBSOMAError(
-            "[SOMAArray::resize] array must be opened in write mode");
+        throw TileDBSOMAError(fmt::format(
+            "{}: array must be opened in write mode",
+            function_name_for_messages));
     }
 
     ArraySchema schema = arr_->schema();
diff --git a/libtiledbsoma/src/soma/soma_array.h b/libtiledbsoma/src/soma/soma_array.h
index 24f00329b9..d6a8508a82 100644
--- a/libtiledbsoma/src/soma/soma_array.h
+++ b/libtiledbsoma/src/soma/soma_array.h
@@ -997,8 +997,7 @@ class SOMAArray : public SOMAObject {
             default:
                 throw std::runtime_error(
                     "internal coding error in "
-                    "SOMAArray::_core_domainish_slot_string: "
-                    "unknown kind");
+                    "SOMAArray::_core_domainish_slot_string: unknown kind");
         }
     }
 
@@ -1061,8 +1060,10 @@ class SOMAArray : public SOMAObject {
      * existing core domain.
      */
     std::pair<bool, std::string> can_resize(
-        const std::vector<int64_t>& newshape) {
-        return _can_set_shape_helper(newshape, true, "resize");
+        const std::vector<int64_t>& newshape,
+        std::string function_name_for_messages) {
+        return _can_set_shape_helper(
+            newshape, true, function_name_for_messages);
     }
 
     /**
@@ -1083,16 +1084,18 @@ class SOMAArray : public SOMAObject {
      * domain.
      */
     std::pair<bool, std::string> can_upgrade_shape(
-        const std::vector<int64_t>& newshape) {
+        const std::vector<int64_t>& newshape,
+        std::string function_name_for_messages) {
         return _can_set_shape_helper(
-            newshape, false, "tiledbsoma_upgrade_shape");
+            newshape, false, function_name_for_messages);
     }
 
     /**
      * This is similar to can_upgrade_shape, but it's a can-we call
      * for maybe_resize_soma_joinid.
      */
-    std::pair<bool, std::string> can_resize_soma_joinid(int64_t newshape);
+    std::pair<bool, std::string> can_resize_soma_joinid_shape(
+        int64_t newshape, std::string function_name_for_messages);
 
     /**
      * @brief Resize the shape (what core calls "current domain") up to the
@@ -1105,7 +1108,9 @@ class SOMAArray : public SOMAObject {
      * @return Nothing. Raises an exception if the resize would be a downsize,
      * which is not supported.
      */
-    void resize(const std::vector<int64_t>& newshape);
+    void resize(
+        const std::vector<int64_t>& newshape,
+        std::string function_name_for_messages);
 
     /**
      * @brief Given an old-style array without current domain, sets its
@@ -1113,7 +1118,9 @@ class SOMAArray : public SOMAObject {
      * of int64 type. Namely, all SparseNDArray/DenseNDArray, and
      * default-indexed DataFrame.
      */
-    void upgrade_shape(const std::vector<int64_t>& newshape);
+    void upgrade_shape(
+        const std::vector<int64_t>& newshape,
+        std::string function_name_for_messages);
 
     /**
      * @brief Increases the tiledbsoma shape up to at most the maxshape,
@@ -1129,7 +1136,8 @@ class SOMAArray : public SOMAObject {
      * @return Throws if the requested shape exceeds the array's create-time
      * maxshape. Throws if the array does not have current-domain support.
      */
-    void resize_soma_joinid_shape(int64_t newshape);
+    void resize_soma_joinid_shape(
+        int64_t newshape, std::string function_name_for_messages);
 
    protected:
     // These two are for use nominally by SOMADataFrame. This could be moved in
@@ -1198,7 +1206,7 @@ class SOMAArray : public SOMAObject {
     /**
      * This is a second-level code-dedupe helper for _can_set_shape_helper.
      */
-    std::pair<bool, std::string> _can_set_shape_domainish_helper(
+    std::pair<bool, std::string> _can_set_shape_domainish_subhelper(
         const std::vector<int64_t>& newshape,
         bool check_current_domain,
         std::string function_name_for_messages);
@@ -1206,7 +1214,9 @@ class SOMAArray : public SOMAObject {
     /**
      * This is a code-dedupe helper method for resize and upgrade_shape.
      */
-    void _set_current_domain_from_shape(const std::vector<int64_t>& newshape);
+    void _set_current_domain_from_shape(
+        const std::vector<int64_t>& newshape,
+        std::string function_name_for_messages);
 
     /**
      * While SparseNDArray, DenseNDArray, and default-indexed DataFrame


Test patch:
diff --git a/libtiledbsoma/test/unit_soma_dataframe.cc b/libtiledbsoma/test/unit_soma_dataframe.cc
index 08c5408c24..4a7f114fe7 100644
--- a/libtiledbsoma/test/unit_soma_dataframe.cc
+++ b/libtiledbsoma/test/unit_soma_dataframe.cc
@@ -472,7 +472,7 @@ TEST_CASE_METHOD(
     sdf->close();
 
     sdf = open(OpenMode::write);
-    sdf->resize_soma_joinid_shape(int64_t{new_max});
+    sdf->resize_soma_joinid_shape(int64_t{new_max}, "testing");
     sdf->close();
 
     sdf = open(OpenMode::write);
@@ -591,7 +591,7 @@ TEST_CASE_METHOD(
 
             sdf = open(OpenMode::write);
             // Array not resizeable if it has not already been sized
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
         } else {
@@ -608,11 +608,11 @@ TEST_CASE_METHOD(
             sdf->close();
 
             sdf = open(OpenMode::read);
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
             sdf = open(OpenMode::write);
-            sdf->resize_soma_joinid_shape(new_shape);
+            sdf->resize_soma_joinid_shape(new_shape, "testing");
             sdf->close();
 
             // Check shape after resize
@@ -655,23 +655,23 @@ TEST_CASE_METHOD(
             REQUIRE(maxdom_sjid[1] > 2000000000);
         }
 
-        // Check can_resize_soma_joinid
-        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid(1);
+        // Check can_resize_soma_joinid_shape
+        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid_shape(
+            1, "testing");
         if (!use_current_domain) {
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "can_resize_soma_joinid: dataframe currently has no domain "
-                "set: please use tiledbsoma_upgrade_domain.");
+                "testing: dataframe currently has no domain set: please "
+                "upgrade the array.");
         } else {
             // Must fail since this is too small.
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "cannot resize_soma_joinid_shape: new soma_joinid shape 1 < "
-                "existing "
-                "shape 199");
-            check = sdf->can_resize_soma_joinid(SOMA_JOINID_RESIZE_DIM_MAX + 1);
+                "testing: new soma_joinid shape 1 < existing shape 199");
+            check = sdf->can_resize_soma_joinid_shape(
+                SOMA_JOINID_RESIZE_DIM_MAX + 1, "testing");
             REQUIRE(check.first == true);
             REQUIRE(check.second == "");
         }
@@ -805,7 +805,7 @@ TEST_CASE_METHOD(
 
             sdf = open(OpenMode::write);
             // Array not resizeable if it has not already been sized
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
         } else {
@@ -821,11 +821,11 @@ TEST_CASE_METHOD(
             sdf->close();
 
             sdf = open(OpenMode::read);
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
             sdf = open(OpenMode::write);
-            sdf->resize_soma_joinid_shape(new_shape);
+            sdf->resize_soma_joinid_shape(new_shape, "testing");
             sdf->close();
 
             // Check shape after resize
@@ -887,23 +887,23 @@ TEST_CASE_METHOD(
             REQUIRE(maxdom_u32[1] > 2000000000);
         }
 
-        // Check can_resize_soma_joinid
-        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid(1);
+        // Check can_resize_soma_joinid_shape
+        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid_shape(
+            1, "testing");
         if (!use_current_domain) {
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "can_resize_soma_joinid: dataframe currently has no domain "
-                "set: please use tiledbsoma_upgrade_domain.");
+                "testing: dataframe currently has no domain set: please "
+                "upgrade the array.");
         } else {
             // Must fail since this is too small.
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "cannot resize_soma_joinid_shape: new soma_joinid shape 1 < "
-                "existing "
-                "shape 199");
-            check = sdf->can_resize_soma_joinid(SOMA_JOINID_RESIZE_DIM_MAX + 1);
+                "testing: new soma_joinid shape 1 < existing shape 199");
+            check = sdf->can_resize_soma_joinid_shape(
+                SOMA_JOINID_RESIZE_DIM_MAX + 1, "testing");
             REQUIRE(check.first == true);
             REQUIRE(check.second == "");
         }
@@ -1055,7 +1055,7 @@ TEST_CASE_METHOD(
 
             sdf = open(OpenMode::write);
             // Array not resizeable if it has not already been sized
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
         } else {
@@ -1071,11 +1071,11 @@ TEST_CASE_METHOD(
             sdf->close();
 
             sdf = open(OpenMode::read);
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
             sdf = open(OpenMode::write);
-            sdf->resize_soma_joinid_shape(new_shape);
+            sdf->resize_soma_joinid_shape(new_shape, "testing");
             sdf->close();
 
             // Check shape after resize
@@ -1135,23 +1135,23 @@ TEST_CASE_METHOD(
 
         REQUIRE(ned_str == std::vector<std::string>({"", ""}));
 
-        // Check can_resize_soma_joinid
-        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid(1);
+        // Check can_resize_soma_joinid_shape
+        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid_shape(
+            1, "testing");
         if (!use_current_domain) {
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "can_resize_soma_joinid: dataframe currently has no domain "
-                "set: please use tiledbsoma_upgrade_domain.");
+                "testing: dataframe currently has no domain set: please "
+                "upgrade the array.");
         } else {
             // Must fail since this is too small.
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "cannot resize_soma_joinid_shape: new soma_joinid shape 1 < "
-                "existing "
-                "shape 99");
-            check = sdf->can_resize_soma_joinid(SOMA_JOINID_RESIZE_DIM_MAX + 1);
+                "testing: new soma_joinid shape 1 < existing shape 99");
+            check = sdf->can_resize_soma_joinid_shape(
+                SOMA_JOINID_RESIZE_DIM_MAX + 1, "testing");
             REQUIRE(check.first == true);
             REQUIRE(check.second == "");
         }
@@ -1286,7 +1286,7 @@ TEST_CASE_METHOD(
 
             sdf = open(OpenMode::write);
             // Array not resizeable if it has not already been sized
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
         } else {
@@ -1297,11 +1297,11 @@ TEST_CASE_METHOD(
             sdf->close();
 
             sdf = open(OpenMode::read);
-            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape));
+            REQUIRE_THROWS(sdf->resize_soma_joinid_shape(new_shape, "testing"));
             sdf->close();
 
             sdf = open(OpenMode::write);
-            sdf->resize_soma_joinid_shape(new_shape);
+            sdf->resize_soma_joinid_shape(new_shape, "testing");
             sdf->close();
 
             // Check shape after resize -- noting soma_joinid is not a dim here
@@ -1344,14 +1344,15 @@ TEST_CASE_METHOD(
             REQUIRE(maxdom_str == std::vector<std::string>({"", ""}));
         }
 
-        // Check can_resize_soma_joinid
-        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid(0);
+        // Check can_resize_soma_joinid_shape
+        std::pair<bool, std::string> check = sdf->can_resize_soma_joinid_shape(
+            0, "testing");
         if (!use_current_domain) {
             REQUIRE(check.first == false);
             REQUIRE(
                 check.second ==
-                "can_resize_soma_joinid: dataframe currently has no domain "
-                "set: please use tiledbsoma_upgrade_domain.");
+                "testing: dataframe currently has no domain set: please "
+                "upgrade the array.");
         } else {
             // Must pass since soma_joinid isn't a dim in this case.
             REQUIRE(check.first == true);
diff --git a/libtiledbsoma/test/unit_soma_dense_ndarray.cc b/libtiledbsoma/test/unit_soma_dense_ndarray.cc
index c55a04ce3c..af116c0d30 100644
--- a/libtiledbsoma/test/unit_soma_dense_ndarray.cc
+++ b/libtiledbsoma/test/unit_soma_dense_ndarray.cc
@@ -147,7 +147,7 @@ TEST_CASE("SOMADenseNDArray: basic", "[SOMADenseNDArray]") {
             //   been sized.
             // * When use_current_domain is true: TODO: current domain not
             //   supported for dense arrays yet (see above).
-            REQUIRE_THROWS(dnda->resize(new_shape));
+            REQUIRE_THROWS(dnda->resize(new_shape, "testing"));
             dnda->close();
         }
     }
diff --git a/libtiledbsoma/test/unit_soma_sparse_ndarray.cc b/libtiledbsoma/test/unit_soma_sparse_ndarray.cc
index fa66faf96e..4d455cedbe 100644
--- a/libtiledbsoma/test/unit_soma_sparse_ndarray.cc
+++ b/libtiledbsoma/test/unit_soma_sparse_ndarray.cc
@@ -146,9 +146,9 @@ TEST_CASE("SOMASparseNDArray: basic", "[SOMASparseNDArray]") {
             // Without current-domain support: this should throw since
             // one cannot resize what has not been sized.
             REQUIRE(!snda->has_current_domain());
-            REQUIRE_THROWS(snda->resize(new_shape));
+            REQUIRE_THROWS(snda->resize(new_shape, "testing"));
             // Now set the shape
-            snda->upgrade_shape(new_shape);
+            snda->upgrade_shape(new_shape, "testing");
             snda->close();
 
             snda->open(OpenMode::read);
@@ -158,7 +158,7 @@ TEST_CASE("SOMASparseNDArray: basic", "[SOMASparseNDArray]") {
             snda->open(OpenMode::write);
             REQUIRE(snda->has_current_domain());
             // Should not fail since we're setting it to what it already is.
-            snda->resize(new_shape);
+            snda->resize(new_shape, "testing");
             snda->close();
 
             snda = SOMASparseNDArray::open(uri, OpenMode::read, ctx);
@@ -171,8 +171,8 @@ TEST_CASE("SOMASparseNDArray: basic", "[SOMASparseNDArray]") {
             snda = SOMASparseNDArray::open(uri, OpenMode::write, ctx);
             // Should throw since this already has a shape (core current
             // domain).
-            REQUIRE_THROWS(snda->upgrade_shape(new_shape));
-            snda->resize(new_shape);
+            REQUIRE_THROWS(snda->upgrade_shape(new_shape, "testing"));
+            snda->resize(new_shape, "testing");
             snda->close();
 
             // Try out-of-bounds write after resize.
@@ -394,21 +394,21 @@ TEST_CASE(
     std::vector<int64_t> newshape_too_big({dim_max + 10});
     std::vector<int64_t> newshape_good({40});
 
-    auto check = snda->can_upgrade_shape(newshape_wrong_dims);
+    auto check = snda->can_upgrade_shape(newshape_wrong_dims, "testing");
     REQUIRE(check.first == false);
     REQUIRE(
         check.second ==
-        "cannot tiledbsoma_upgrade_shape: provided shape has ndim 2, while the "
+        "testing: provided shape has ndim 2, while the "
         "array has 1");
 
-    check = snda->can_upgrade_shape(newshape_too_big);
+    check = snda->can_upgrade_shape(newshape_too_big, "testing");
     REQUIRE(check.first == false);
     REQUIRE(
         check.second ==
-        "cannot tiledbsoma_upgrade_shape for soma_dim_0: new 1009 < maxshape "
+        "testing for soma_dim_0: new 1009 < maxshape "
         "1000");
 
-    check = snda->can_upgrade_shape(newshape_good);
+    check = snda->can_upgrade_shape(newshape_good, "testing");
     REQUIRE(check.first == true);
     REQUIRE(check.second == "");
 }
@@ -462,19 +462,18 @@ TEST_CASE("SOMASparseNDArray: can_resize", "[SOMASparseNDArray]") {
     std::vector<int64_t> newshape_too_small({40});
     std::vector<int64_t> newshape_good({2000});
 
-    auto check = snda->can_resize(newshape_wrong_dims);
+    auto check = snda->can_resize(newshape_wrong_dims, "testing");
     REQUIRE(check.first == false);
     REQUIRE(
         check.second ==
-        "cannot resize: provided shape has ndim 2, while the array has 1");
+        "testing: provided shape has ndim 2, while the array has 1");
 
-    check = snda->can_resize(newshape_too_small);
+    check = snda->can_resize(newshape_too_small, "testing");
     REQUIRE(check.first == false);
     REQUIRE(
-        check.second ==
-        "cannot resize for soma_dim_0: new 40 < existing shape 1000");
+        check.second == "testing for soma_dim_0: new 40 < existing shape 1000");
 
-    check = snda->can_resize(newshape_good);
+    check = snda->can_resize(newshape_good, "testing");
     REQUIRE(check.first == true);
     REQUIRE(check.second == "");
 }