nathanmarz/storm · critical · RuntimeException

Blowfish encryption key not specified

Error message

Blowfish encryption key not specified

What it means

BlowfishTupleSerializer encrypts serialized tuples with Kryo's BlowfishSerializer using a hex-encoded key taken from the Storm config key StormBoltEnviroment... specifically its SECRET_KEY config constant (topology.message.transfer...). If storm_conf has no value for that key, the constructor deliberately throws this RuntimeException because encryption without a key is impossible — the serializer cannot be constructed.

Solutions

  1. Set the secret key in storm.yaml (the constant referenced by BlowfishTupleSerializer.SECRET_KEY, e.g. 'supervisor...'), as a valid hex string.
  2. Generate a proper hex key, e.g. run 'openssl rand -hex 16' and paste the result under the config key.
  3. Make sure the key is present on all workers (storm.yaml is distributed to every node) so the topology can serialize/deserialize on both ends.
  4. If encryption is not needed, remove the BlowfishTupleSerializer registration instead of registering it without a key.

Example fix

// storm.yaml before
storm.messaging.serializer: "backtype.storm.security.serialization.BlowfishTupleSerializer"
// after
storm.messaging.serializer: "backtype.storm.security.serialization.BlowfishTupleSerializer"
topology.tuple.secret.key: "3f8a1c9d2b7e4f6a1c9d2b7e4f6a1c9d"
Defensive patterns

Strategy: validation

Validate before calling

String key = (String) stormConf.get("topology.tuple.secret.key");
if (key == null || key.isEmpty()) {
    throw new IllegalArgumentException("Blowfish secret key must be set in storm.yaml before using BlowfishTupleSerializer");
}
if (!key.matches("[0-9a-fA-F]+") || key.length() % 2 != 0) {
    throw new IllegalArgumentException("Blowfish secret key must be a valid even-length hex string");
}

Try / catch

try {
    new BlowfishTupleSerializer(kryo, stormConf);
} catch (RuntimeException e) {
    LOG.error("Blowfish serializer setup failed: " + e.getMessage());
    throw e; // fail fast: continuing would silently disable encryption
}

Prevention

When it happens

Trigger: Configuring storm.messaging.serializer / topology.tuple.serializer (or the Kryo decorator registration) to BlowfishTupleSerializer without setting the corresponding storm.conf key (SECRETS... 'topology.tuple.secret.key' / 'storm.messaging...secret.key') in storm.yaml; the key was removed during config migration; or the config map passed programmatically omits it.

Common situations: Operators enabling encrypted tuple transport per the Storm security docs but forgetting to add the secret key to storm.yaml on workers; upgrading Storm and the old key name no longer matches; building Kryo instances in unit tests with a stripped/minimal conf map.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at storm-core/src/jvm/backtype/storm/security/serialization/BlowfishTupleSerializer.java:55

/**
 * Apply Blowfish encrption for tuple communication to bolts
 */
public class BlowfishTupleSerializer extends Serializer<ListDelegate> {
    /**
     * The secret key (if any) for data encryption by blowfish payload serialization factory (BlowfishSerializationFactory). 
     * You should use in via "storm -c topology.tuple.serializer.blowfish.key=YOURKEY -c topology.tuple.serializer=backtype.storm.security.serialization.BlowfishTupleSerializer jar ...".
     */
    public static String SECRET_KEY = "topology.tuple.serializer.blowfish.key";
    private static final Logger LOG = Logger.getLogger(BlowfishTupleSerializer.class);
    private BlowfishSerializer _serializer;

    public BlowfishTupleSerializer(Kryo kryo, Map storm_conf) {
        String encryption_key = null;
        try {
            encryption_key = (String)storm_conf.get(SECRET_KEY);
            LOG.debug("Blowfish serializer being constructed ...");
            if (encryption_key == null) {
                throw new RuntimeException("Blowfish encryption key not specified");
            }
            byte[] bytes =  Hex.decodeHex(encryption_key.toCharArray());
            _serializer = new BlowfishSerializer(new ListDelegateSerializer(), bytes);
        } catch (org.apache.commons.codec.DecoderException ex) {
            throw new RuntimeException("Blowfish encryption key invalid", ex);
        }
    }

    @Override
    public void write(Kryo kryo, Output output, ListDelegate object) {
        _serializer.write(kryo, output, object);
    }

    @Override
    public ListDelegate read(Kryo kryo, Input input, Class<ListDelegate> type) {
        return (ListDelegate)_serializer.read(kryo, input, type);
    }

View on GitHub (pinned to cdb116e942)