Summary
When a user calls export_roi / export_roi_to_quicfire with features_config={'createRoadFeatures': False, 'createWaterFeatures': False}, the SDK correctly skips creating the road and water features — but it still asks the API to use those features as featureMasks in the surface grid and tree inventory. The downstream API calls then reference features that were never created, which causes the export to fail (or, in the road case, hang until the API timeout).
This was reported by a QUIC-Fire GUI user who supplied exactly that features_config and got a traceback anyway. They also separately reported the road feature getting stuck on RUNNING for ~5 minutes before timing out on the default path; that report is consistent with the same masking dependency cascading from a backend road-processing regression — the default surface/tree-inventory pipelines both wait on the road feature because of the hardcoded masks.
Reproduction
from fastfuels_sdk import export_roi_to_quicfire
export_roi_to_quicfire(
roi=roi,
export_path=\"out.zip\",
features_config={
\"createRoadFeatures\": False,
\"createWaterFeatures\": False,
},
)
Expected: surface grid / tree inventory are built without any road or water feature masking.
Actual: surface grid and tree inventory are built with featureMasks=[\"road\", \"water\"], so the API job fails (or hangs) because the masks reference features that don't exist for this domain.
Root cause
fastfuels_sdk/convenience.py propagates createRoadFeatures / createWaterFeatures only to the feature-creation block (lines 624-636) and to the feature grid (lines 644-657, which correctly ends up with feature_grid = None). The hardcoded \"featureMasks\": [\"road\", \"water\"] entries in the other defaults are never sanitized:
DEFAULT_SURFACE_CONFIG — fuelLoad (line 76), fuelDepth (line 84), fuelMoisture (line 90)
DEFAULT_TREE_INVENTORY_CONFIG (line 109)
Those merged configs are then handed to _configure_surface_builder and _create_tree_inventory, which forward the masks to SurfaceGridBuilder.with_* and Inventories.create_tree_inventory_from_treemap respectively — even when both feature-creation flags are False.
Proposed fix
After merging user configs with defaults, sanitize the featureMasks lists on merged_surface_config (fuelLoad, fuelDepth, fuelMoisture) and merged_tree_inventory_config so that:
\"road\" is stripped whenever createRoadFeatures is False
\"water\" is stripped whenever createWaterFeatures is False
This should run after the user merge so an explicit user-supplied featureMasks is also sanitized — the user should not be able to mask against a feature that won't exist.
Add a regression test in tests/test_convenience_config.py that runs export_roi_to_quicfire with both flags False and asserts that no feature_masks containing \"road\" or \"water\" are passed to any builder or inventory call.
Workaround for affected users
Until the fix lands, users disabling road/water features must also clear featureMasks everywhere they appear. For example:
features_config = {\"createRoadFeatures\": False, \"createWaterFeatures\": False}
surface_config = {
\"fuelLoad\": {\"featureMasks\": []},
\"fuelDepth\": {\"featureMasks\": []},
\"fuelMoisture\": {\"featureMasks\": []},
}
tree_inventory_config = {\"featureMasks\": []}
Summary
When a user calls
export_roi/export_roi_to_quicfirewithfeatures_config={'createRoadFeatures': False, 'createWaterFeatures': False}, the SDK correctly skips creating the road and water features — but it still asks the API to use those features asfeatureMasksin the surface grid and tree inventory. The downstream API calls then reference features that were never created, which causes the export to fail (or, in the road case, hang until the API timeout).This was reported by a QUIC-Fire GUI user who supplied exactly that
features_configand got a traceback anyway. They also separately reported the road feature getting stuck onRUNNINGfor ~5 minutes before timing out on the default path; that report is consistent with the same masking dependency cascading from a backend road-processing regression — the default surface/tree-inventory pipelines both wait on the road feature because of the hardcoded masks.Reproduction
Expected: surface grid / tree inventory are built without any road or water feature masking.
Actual: surface grid and tree inventory are built with
featureMasks=[\"road\", \"water\"], so the API job fails (or hangs) because the masks reference features that don't exist for this domain.Root cause
fastfuels_sdk/convenience.pypropagatescreateRoadFeatures/createWaterFeaturesonly to the feature-creation block (lines 624-636) and to the feature grid (lines 644-657, which correctly ends up withfeature_grid = None). The hardcoded\"featureMasks\": [\"road\", \"water\"]entries in the other defaults are never sanitized:DEFAULT_SURFACE_CONFIG—fuelLoad(line 76),fuelDepth(line 84),fuelMoisture(line 90)DEFAULT_TREE_INVENTORY_CONFIG(line 109)Those merged configs are then handed to
_configure_surface_builderand_create_tree_inventory, which forward the masks toSurfaceGridBuilder.with_*andInventories.create_tree_inventory_from_treemaprespectively — even when both feature-creation flags areFalse.Proposed fix
After merging user configs with defaults, sanitize the
featureMaskslists onmerged_surface_config(fuelLoad,fuelDepth,fuelMoisture) andmerged_tree_inventory_configso that:\"road\"is stripped whenevercreateRoadFeaturesisFalse\"water\"is stripped whenevercreateWaterFeaturesisFalseThis should run after the user merge so an explicit user-supplied
featureMasksis also sanitized — the user should not be able to mask against a feature that won't exist.Add a regression test in
tests/test_convenience_config.pythat runsexport_roi_to_quicfirewith both flagsFalseand asserts that nofeature_maskscontaining\"road\"or\"water\"are passed to any builder or inventory call.Workaround for affected users
Until the fix lands, users disabling road/water features must also clear
featureMaskseverywhere they appear. For example: