@@ -109,12 +109,21 @@ public void onApplicationEvent(RqueueBootstrapEvent event) {
109109 }
110110 }
111111 if (!failures .isEmpty ()) {
112+ String hint = config .isAutoCreateStreams ()
113+ ? "Stream creation failed — verify NATS is running with JetStream enabled"
114+ + " (start the server with `nats-server -js`) and that the account has"
115+ + " `add_stream` permission."
116+ : "With rqueue.nats.auto-create-streams=false every required stream must exist"
117+ + " before the application starts. Run `nats stream add` for each missing"
118+ + " stream or set rqueue.nats.auto-create-streams=true to let rqueue create"
119+ + " them automatically." ;
112120 throw new IllegalStateException ("NATS JetStream provisioning failed for "
113121 + failures .size ()
114122 + " of "
115123 + total
116- + " stream(s) at startup. With rqueue.nats.autoCreateStreams=false, every required"
117- + " stream must exist before the application starts. Failed streams:\n "
124+ + " stream(s) at startup. "
125+ + hint
126+ + " Failed streams:\n "
118127 + " - "
119128 + String .join ("\n - " , failures ));
120129 }
@@ -129,7 +138,7 @@ private int tryEnsure(List<String> failures, String streamName, String subject)
129138 provisioner .ensureStream (streamName , List .of (subject ));
130139 return 1 ;
131140 } catch (RqueueNatsException e ) {
132- failures .add (streamName + " (subject " + subject + "): " + e . getMessage ( ));
141+ failures .add (streamName + " (subject " + subject + "): " + rootCause ( e ));
133142 return 1 ;
134143 }
135144 }
@@ -139,8 +148,18 @@ private int tryEnsureDlq(List<String> failures, String dlqStream, String dlqSubj
139148 provisioner .ensureDlqStream (dlqStream , List .of (dlqSubject ));
140149 return 1 ;
141150 } catch (RqueueNatsException e ) {
142- failures .add (dlqStream + " (DLQ subject " + dlqSubject + "): " + e . getMessage ( ));
151+ failures .add (dlqStream + " (DLQ subject " + dlqSubject + "): " + rootCause ( e ));
143152 return 1 ;
144153 }
145154 }
155+
156+ /** Returns the deepest non-null message in the cause chain for diagnostics. */
157+ private static String rootCause (Throwable t ) {
158+ Throwable cause = t ;
159+ while (cause .getCause () != null ) {
160+ cause = cause .getCause ();
161+ }
162+ String msg = cause .getMessage ();
163+ return (msg != null && !msg .isEmpty ()) ? msg : cause .getClass ().getSimpleName ();
164+ }
146165}
0 commit comments