{"record":{"id":"c9ca76fc56f8bfa9","repo":"quickwit-oss/quickwit","slug":"storage-factory-and-config-backends-should-match","errorCode":null,"errorMessage":"storage factory and config backends should match","messagePattern":"storage factory and config backends should match","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-storage/src/storage_resolver.rs","lineNumber":134,"sourceCode":"        }\n        #[cfg(feature = \"gcs\")]\n        {\n            builder = builder.register(GoogleCloudStorageFactory::new(\n                storage_configs.find_google().cloned().unwrap_or_default(),\n            ));\n        }\n        #[cfg(not(feature = \"gcs\"))]\n        {\n            use crate::storage_factory::UnsupportedStorage;\n\n            builder = builder.register(UnsupportedStorage::new(\n                StorageBackend::Google,\n                \"Quickwit was compiled without the `gcs` feature\",\n            ))\n        }\n        builder\n            .build()\n            .expect(\"storage factory and config backends should match\")\n    }\n\n    /// Returns a [`StorageResolver`] for testing purposes. Unlike\n    /// [`StorageResolver::unconfigured`], this resolver does not return a singleton.\n    #[cfg(any(test, feature = \"testsuite\"))]\n    pub fn for_test() -> Self {\n        StorageResolver::builder()\n            .register(RamStorageFactory::default())\n            .register(LocalFileStorageFactory)\n            .build()\n            .expect(\"storage factory and config backends should match\")\n    }\n}\n\n#[derive(Default)]\npub struct StorageResolverBuilder {\n    per_backend_factories: HashMap<StorageBackend, Box<dyn StorageFactory>>,\n}","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-storage/src/storage_resolver.rs#L116-L152","documentation":"This panic is thrown by `StorageResolver::configured` after building the storage resolver from the set of storage backends compiled into the binary. It means the internal invariant that every `StorageBackend` variant declared in the configuration (File, S3, Azure, Google) has a corresponding registered storage factory was violated. Since the resolver builder registers a factory for every backend enabled by cargo features (the code right above panics on backends missing their feature), this can only fire if a new backend variant was added to `StorageBackend` without registering a factory, or feature/build wiring diverged. It is an assertion of compile-time/feature consistency, not a runtime misconfiguration the user can cause.","triggerScenarios":"Calling `StorageResolver::configured(...)` when the resolver builder's registry does not contain a factory for every `StorageBackend` variant derivable from config — e.g. the binary was built so a config-visible backend (like `gcs`) is absent and the earlier `expect` did not cover it, or a contributor added a new `StorageBackend` variant without adding it to the builder in `configured`.","commonSituations":"A developer adds support for a new storage backend to the `StorageBackend` enum but forgets to register its factory in `configured`; a custom build with unusual feature flags leaves a backend enum variant unregistered; downstream forks patching the enum or feature gates.","solutions":["Check the cargo features used to build Quickwit: rebuild with the feature matching your config backend enabled (e.g. `gcs` for StorageBackend::Google) — the preceding expect already guards this, so if you hit this one your build is inconsistent","If you added a new `StorageBackend` variant, register a corresponding factory in the `StorageResolver::configured` builder in quickwit/quickwit-storage/src/storage_resolver.rs","Rebuild from a clean checkout of an unmodified Quickwit release; a patched or stale build is the usual cause","If it reproduces on unmodified code, file a bug — this is an internal invariant violation"],"exampleFix":"// before (new backend variant added without factory)\nStorageBackend::MyNewBackend => unreachable!()\n// after\nbuilder = builder.register(\n    StorageBackend::MyNewBackend,\n    StorageFactoryUri::try_new(\"mynewbackend\")?,\n    Arc::new(MyNewBackendFactory::default()),\n);","handlingStrategy":"validation","validationCode":"// Before relying on a configured resolver, check the backend you need is registered\nlet resolver = StorageResolver::configured(&storage_configs)?; // prefer ?-propagation upstream; if you must call expect paths, verify feature gates first\nassert!(\n    cfg!(feature = \"gcs\") || storage_configs.google.is_none(),\n    \"gcs config present but binary compiled without gcs feature\"\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When adding a StorageBackend variant, immediately register its factory in StorageResolver::configured and add a unit test that resolves every backend","Build with the full set of storage features (`--all-features`) in CI to surface unregistered backends","Never patch the StorageBackend enum in forks without updating the resolver builder"],"tags":["rust","storage","invariant","build-configuration"],"backgroundTag":"internal-invariant-violation","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}