@@ -1124,15 +1124,17 @@ describe("Trace Helper - setOrchestrationStatusFromActions", () => {
11241124 expect ( spans [ 0 ] . status . code ) . toBe ( otel . SpanStatusCode . OK ) ;
11251125 } ) ;
11261126
1127- it ( "should set status attribute for failed orchestration and ERROR span status" , ( ) => {
1127+ it ( "should set status attribute for failed orchestration and ERROR span status with failureDetails " , ( ) => {
11281128 const tracer = otel . trace . getTracer ( TRACER_NAME ) ;
11291129 const span = tracer . startSpan ( "orch-status-failed" ) ;
11301130
1131+ // Match the real runtime path: setFailed() sets failureDetails but NOT result
11311132 const completeAction = new pb . CompleteOrchestrationAction ( ) ;
11321133 completeAction . setOrchestrationstatus ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
1133- const resultValue = new StringValue ( ) ;
1134- resultValue . setValue ( "User code threw an error" ) ;
1135- completeAction . setResult ( resultValue ) ;
1134+ const failureDetails = new pb . TaskFailureDetails ( ) ;
1135+ failureDetails . setErrormessage ( "User code threw an error" ) ;
1136+ failureDetails . setErrortype ( "Error" ) ;
1137+ completeAction . setFailuredetails ( failureDetails ) ;
11361138
11371139 const action = new pb . OrchestratorAction ( ) ;
11381140 action . setCompleteorchestration ( completeAction ) ;
@@ -1146,6 +1148,74 @@ describe("Trace Helper - setOrchestrationStatusFromActions", () => {
11461148 expect ( spans [ 0 ] . status . message ) . toBe ( "User code threw an error" ) ;
11471149 } ) ;
11481150
1151+ it ( "should fall back to result field for failed orchestration error message" , ( ) => {
1152+ const tracer = otel . trace . getTracer ( TRACER_NAME ) ;
1153+ const span = tracer . startSpan ( "orch-status-failed-result-fallback" ) ;
1154+
1155+ // Edge case: no failureDetails, but result is set (backwards compatibility)
1156+ const completeAction = new pb . CompleteOrchestrationAction ( ) ;
1157+ completeAction . setOrchestrationstatus ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
1158+ const resultValue = new StringValue ( ) ;
1159+ resultValue . setValue ( "Legacy error message" ) ;
1160+ completeAction . setResult ( resultValue ) ;
1161+
1162+ const action = new pb . OrchestratorAction ( ) ;
1163+ action . setCompleteorchestration ( completeAction ) ;
1164+
1165+ setOrchestrationStatusFromActions ( span , [ action ] ) ;
1166+ span . end ( ) ;
1167+
1168+ const spans = exporter . getFinishedSpans ( ) ;
1169+ expect ( spans [ 0 ] . status . code ) . toBe ( otel . SpanStatusCode . ERROR ) ;
1170+ expect ( spans [ 0 ] . status . message ) . toBe ( "Legacy error message" ) ;
1171+ } ) ;
1172+
1173+ it ( "should use default error message when neither failureDetails nor result is set" , ( ) => {
1174+ const tracer = otel . trace . getTracer ( TRACER_NAME ) ;
1175+ const span = tracer . startSpan ( "orch-status-failed-no-details" ) ;
1176+
1177+ const completeAction = new pb . CompleteOrchestrationAction ( ) ;
1178+ completeAction . setOrchestrationstatus ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
1179+
1180+ const action = new pb . OrchestratorAction ( ) ;
1181+ action . setCompleteorchestration ( completeAction ) ;
1182+
1183+ setOrchestrationStatusFromActions ( span , [ action ] ) ;
1184+ span . end ( ) ;
1185+
1186+ const spans = exporter . getFinishedSpans ( ) ;
1187+ expect ( spans [ 0 ] . status . code ) . toBe ( otel . SpanStatusCode . ERROR ) ;
1188+ expect ( spans [ 0 ] . status . message ) . toBe ( "Orchestration failed" ) ;
1189+ } ) ;
1190+
1191+ it ( "should preserve an explicitly empty failureDetails message over the result/default fallback" , ( ) => {
1192+ const tracer = otel . trace . getTracer ( TRACER_NAME ) ;
1193+ const span = tracer . startSpan ( "orch-status-failed-empty-message" ) ;
1194+
1195+ // failureDetails is present (orchestration failed) but the error message is
1196+ // empty. failureDetails is authoritative, so nullish coalescing must keep the
1197+ // empty string rather than falling through to result/"Orchestration failed".
1198+ const completeAction = new pb . CompleteOrchestrationAction ( ) ;
1199+ completeAction . setOrchestrationstatus ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
1200+ const failureDetails = new pb . TaskFailureDetails ( ) ;
1201+ failureDetails . setErrormessage ( "" ) ;
1202+ failureDetails . setErrortype ( "Error" ) ;
1203+ completeAction . setFailuredetails ( failureDetails ) ;
1204+ const resultValue = new StringValue ( ) ;
1205+ resultValue . setValue ( "Should not be used" ) ;
1206+ completeAction . setResult ( resultValue ) ;
1207+
1208+ const action = new pb . OrchestratorAction ( ) ;
1209+ action . setCompleteorchestration ( completeAction ) ;
1210+
1211+ setOrchestrationStatusFromActions ( span , [ action ] ) ;
1212+ span . end ( ) ;
1213+
1214+ const spans = exporter . getFinishedSpans ( ) ;
1215+ expect ( spans [ 0 ] . status . code ) . toBe ( otel . SpanStatusCode . ERROR ) ;
1216+ expect ( spans [ 0 ] . status . message ) . toBe ( "" ) ;
1217+ } ) ;
1218+
11491219 it ( "should not set status when no completion action present" , ( ) => {
11501220 const tracer = otel . trace . getTracer ( TRACER_NAME ) ;
11511221 const span = tracer . startSpan ( "orch-status-none" ) ;
0 commit comments