Skip to content

Warnings triggered when generating ComplexHeatmap inside future_promise() #36

@charleschuang1993

Description

@charleschuang1993

🚨 Issue: Warnings triggered when generating ComplexHeatmap inside future_promise()

Labels: bug parallel ComplexHeatmap future
Component: RNAseqShinyApp – Heatmap Rendering Module


🧾 Summary

When executing make_heatmap_mae() inside future_promise() (with multisession plan), several warnings and messages are triggered at runtime:

🔍 Full warning message
'use_raster' is automatically set to TRUE for a matrix with more than 2000 rows.  
'magick' package is suggested to install to give better rasterization.  
The automatically generated colors map from the 1^st and 99^th of the values in the matrix.  
You can manually set the color to `col` argument.

Warning: MultisessionFuture (NULL) added, removed, or modified devices.  
A future expression must close any opened devices and must not close devices it did not open.  

Warning: UNRELIABLE VALUE: Future (NULL) unexpectedly generated random numbers  
without specifying argument 'seed'. There is a risk that those random numbers are not statistically sound  
and the overall results might be invalid. To fix this, specify 'seed=TRUE'.

[2025-04-18 06:08:26.109379] initialize the original heatmap (ID: ht) and calculate positions.  
[1] "not saving the figure"

🧭 Root Cause Analysis

Message / Warning Cause Problem
use_raster, magick, color scale notices Informational from ComplexHeatmap Cosmetic only
⚠️ devices differ draw() is called inside future_promise() → opens graphics device in worker Device not closed in correct thread; causes leakage/mismatch
⚠️ UNRELIABLE VALUE Random numbers generated in worker without seed Results non-reproducible

🧪 Current Behavior

future_promise({
  make_heatmap_mae(mae, geneListVec)    # internally calls draw()
  on.exit(dev.off())                    # may or may not close the right device
}, seed = TRUE) %...>% (function(ht) {
  if (!is.null(ht)) {
    makeInteractiveComplexHeatmap(input, output, session, ht, "ht")
  } else {
    output$ht_heatmap <- renderPlot({
      grid::grid.text("No data available.")
    })
  }
})

✅ Recommended Fix

  • Refactor make_heatmap_mae() to add a draw_ht = FALSE parameter
    • When draw_ht = FALSE, only build and return the Heatmap object without calling draw()
    • This prevents opening any graphics device in a background thread
# Inside make_heatmap_mae()
if (isTRUE(draw_ht)) {
  ht <- draw(ht, merge_legend = TRUE)
}
return(ht)
  • In future_promise(), only construct the heatmap object
future_promise({
  make_heatmap_mae(mae, geneListVec, draw_ht = FALSE)
}, seed = TRUE) %...>% (function(ht) {
  makeInteractiveComplexHeatmap(
    input, output, session,
    ComplexHeatmap::draw(ht, merge_legend = TRUE),
    heatmap_id = "ht"
  )
})
  • Optionally suppress non-critical ComplexHeatmap messages
ComplexHeatmap::ht_opt$message <- FALSE

✅ Acceptance Criteria

  • No "MultisessionFuture modified devices" warning in logs
  • No "UNRELIABLE VALUE: … random numbers" warning appears
  • Heatmap renders correctly in Shiny and is fully interactive
  • Results are consistent across multiple runs with same input + seed
  • ht object is returned correctly to UI

🧩 Notes

  • Drawing plots inside parallel workers (via draw()) opens graphics devices outside the main R session, which is unsafe.
  • Random number generation in parallel processes requires explicit seed = TRUE to ensure reproducibility.
  • Returning a draw()-free Heatmap object significantly reduces serialization size and improves performance.
  • If a static plot is needed, consider returning a PNG path instead, or rendering with renderImage().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions