Skip to content

Fix coercing a v5 assay that has no feature metadata - #300

Closed
BenjaminDEMAILLE wants to merge 1 commit into
satijalab:mainfrom
BenjaminDEMAILLE:fix/assay-empty-feature-meta
Closed

BenjaminDEMAILLE wants to merge 1 commit into
satijalab:mainfrom
BenjaminDEMAILLE:fix/assay-empty-feature-meta

Conversation

@BenjaminDEMAILLE

Copy link
Copy Markdown

Fixes satijalab/seurat#9138.

The bug

Converting a v5 assay to a v3 assay fails when no feature-level metadata has been computed yet:

obj <- CreateSeuratObject(counts = data)
obj <- NormalizeData(obj)
obj[["RNA"]] <- methods::as(obj[["RNA"]], Class = "Assay")
#> Error in .subset2(x, i, exact = exact) : subscript out of bounds

Running FindVariableFeatures() first makes it work, which is what made the report read as a variable-features problem. It is not. FindVariableFeatures() simply happens to be what first populates feature metadata: before it, assay[[]] is a data frame of n features and zero columns; after it, eight.

Root cause

The coercion copies feature metadata with to[[]] <- from[[]], and [[<- unpacks the incoming frame column by column:

value <- lapply(
  X = 1:ncol(x = value),
  FUN = function(index) { ... value[[index]] ... }
)

For zero columns 1:0 is c(1, 0), so the first iteration asks for value[[1]] of a frame that has no columns. Hence subscript out of bounds, from a loop that should not have run at all.

The change

seq_len(ncol(value)).

I audited the remaining 1:ncol / 1:nrow / 1:length loops in the package rather than assume this was the only one: the others are either inside a length(i) > 1 guard or not reachable with an empty input, so this is the only site changed.

Verification

main this PR
as(assay5, "Assay") with no feature metadata subscript out of bounds succeeds, counts and data preserved
same, after FindVariableFeatures succeeds succeeds, 200 variable features kept

Tests

New tests/testthat/test_assay_meta_features.R: assigning an empty feature metadata frame is a no-op rather than an error, a v5 assay with no feature metadata coerces to v3 with its matrices intact, metadata that is present is still carried across, and assigning a non-empty frame still replaces the metadata.

Against main: 2 failures and 3 errors. With this change: 15 passes. Full suite 191 passing, R CMD check Status OK.

🤖 Generated with Claude Code

Converting a v5 assay to a v3 assay fails when no feature-level metadata
has been computed yet:

  obj <- CreateSeuratObject(counts = data)
  obj <- NormalizeData(obj)
  obj[["RNA"]] <- as(obj[["RNA"]], Class = "Assay")
  #> Error in .subset2(x, i, exact = exact) : subscript out of bounds

Running FindVariableFeatures() first avoids it, which is what made the
report look like a variable-features problem. It is not: FindVariableFeatures
happens to be what first populates feature metadata.

The coercion copies feature metadata with `to[[]] <- from[[]]`. With no
metadata that value is a data frame of n features and zero columns, and
`[[<-` walks it with

  lapply(X = 1:ncol(x = value), ...)

For zero columns `1:0` is c(1, 0), so the first iteration asks for
value[[1]] of a frame that has no columns. Use seq_len().

Before: error. After: the conversion succeeds with counts and data
preserved, and the case that already worked is unchanged, variable
features included.

An audit of the remaining `1:ncol`/`1:nrow`/`1:length` loops in the package
found the others either guarded by a length check or not reachable with an
empty input, so this is the only site changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BenjaminDEMAILLE

Copy link
Copy Markdown
Author

Superseded by #318, which groups the correctness PRs into one so that a single review covers the subject. The commit is carried over unchanged; closing here to keep the queue down rather than because anything was wrong with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug in methods::as(pbmc3k.final3[["RNA"]], Class = "Assay")

1 participant