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
- Set the secret key in storm.yaml (the constant referenced by BlowfishTupleSerializer.SECRET_KEY, e.g. 'supervisor...'), as a valid hex string.
- Generate a proper hex key, e.g. run 'openssl rand -hex 16' and paste the result under the config key.
- 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.
- 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
- Add the secret-key entry to storm.yaml wherever the Blowfish serializer is registered, and distribute it to every node
- Generate keys with 'openssl rand -hex 16' to avoid formatting mistakes
- Add a config sanity check to your deployment pipeline that greps storm.yaml for the key when the serializer is enabled
- Keep the key out of logs and source control; supply it via secrets management
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
- Blowfish encryption key invalid
- Could not find a ' ' entry in this configuration: Client…
- Could not find a ' ' entry in this configuration: Server…
- Unable to create serializer
- Cannot set serializations for a component using fluent API
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)