nathanmarz/storm · error · RuntimeException

Found multiple resources. You're probably bundling the…

Error message

Found multiple ${name} resources. You're probably bundling the Storm jars with your topology jar. ${resources}

What it means

findAndReadConfigFile throws this RuntimeException when the classpath search for the named config resource returns more than one URL. Storm refuses to guess which copy to load because duplicate Storm jars (or stray config resources) on the classpath make the effective configuration ambiguous.

Solutions

  1. Mark storm-core as <scope>provided</scope> (or exclude it) in your topology build so Storm's own jars supply the config resources exactly once.
  2. Inspect the resource list printed in the exception message and remove the offending jar(s) from the classpath (e.g. delete the bundled copy from your topology jar).
  3. Rebuild the uber jar with exclusion filters (maven-shade <excludes> or Gradle 'exclude group') that drop *.yaml from Storm packages.
  4. As a last resort, keep a single canonical copy of the config resource in your jar and remove the duplicate elsewhere.

Example fix

<!-- before: bundles Storm and its configs -->
<dependency>
  <groupId>org.apache.storm</groupId>
  <artifactId>storm-core</artifactId>
</dependency>
<!-- after -->
<dependency>
  <groupId>org.apache.storm</groupId>
  <artifactId>storm-core</artifactId>
  <scope>provided</scope>
</dependency>
Defensive patterns

Strategy: validation

Validate before calling

Set<URL> hits = Utils.findResources("storm.yaml");
if (hits.size() > 1) {
    throw new IllegalStateException("Duplicate storm.yaml on classpath: " + hits + " — unshade your topology jar");
}

Try / catch

try {
    conf = Utils.readStormConfig();
} catch (RuntimeException e) {
    if (e.getMessage().startsWith("Found multiple")) {
        throw new IllegalStateException("Uber jar bundles Storm resources; set storm-core to provided scope", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Utils.findAndReadConfigFile(name, ...) (or readDefaultConfig/readStormConfig) invoked while the classpath contains the same config resource (e.g. storm.yaml or defaults.yaml) in two or more locations — typically because the user's topology uber-jar bundles Storm's classes and resources alongside the Storm installation jars.

Common situations: Building an uber/fat jar with maven-shade that includes storm-core's defaults.yaml/storm.yaml; adding storm-core as a non-provided dependency in a topology project; manually copying storm jars into the topology lib directory; duplicate config files in multiple jar entries.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of nathanmarz/storm@cdb116e942 (2026-09-12). Data as JSON: /api/errors/3cef6c428f6bf400. Report an issue: GitHub.

Appendix: source

Thrown at storm-core/src/jvm/backtype/storm/utils/Utils.java:133

            List<URL> ret = new ArrayList<URL>();
            while(resources.hasMoreElements()) {
                ret.add(resources.nextElement());
            }
            return ret;
        } catch(IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static Map findAndReadConfigFile(String name, boolean mustExist) {
        try {
            HashSet<URL> resources = new HashSet<URL>(findResources(name));
            if(resources.isEmpty()) {
                if(mustExist) throw new RuntimeException("Could not find config file on classpath " + name);
                else return new HashMap();
            }
            if(resources.size() > 1) {
                throw new RuntimeException("Found multiple " + name + " resources. You're probably bundling the Storm jars with your topology jar. "
                  + resources);
            }
            URL resource = resources.iterator().next();
            Yaml yaml = new Yaml();
            Map ret = (Map) yaml.load(new InputStreamReader(resource.openStream()));
            if(ret==null) ret = new HashMap();
            

            return new HashMap(ret);
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static Map findAndReadConfigFile(String name) {
       return findAndReadConfigFile(name, true);
    }

View on GitHub (pinned to cdb116e942)