@@ -24,6 +24,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
2424 const [ uploadedTaskId , setUploadedTaskId ] = useState < string | null > ( null ) ;
2525 const [ shouldCreateFilter , setShouldCreateFilter ] = useState ( false ) ;
2626 const [ isCreatingFilter , setIsCreatingFilter ] = useState ( false ) ;
27+ const [ error , setError ] = useState < string | null > ( null ) ;
2728
2829 const createFilterMutation = useCreateFilter ( ) ;
2930 const updateOnboardingMutation = useUpdateOnboardingStateMutation ( ) ;
@@ -63,6 +64,41 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
6364 matchingTask . status === "running" ||
6465 matchingTask . status === "processing" ;
6566
67+ // Check if matching task failed or has error status
68+ const failedTask =
69+ matchingTask . status === "failed" || matchingTask . status === "error" ;
70+
71+ // Check if any file inside the task failed
72+ const filesArray = matchingTask . files
73+ ? ( Object . values ( matchingTask . files ) as {
74+ status : string ;
75+ error ?: string ;
76+ } [ ] )
77+ : [ ] ;
78+ const hasFailedFile = filesArray . some (
79+ ( file ) => file . status === "failed" || file . status === "error" ,
80+ ) ;
81+
82+ if ( failedTask || hasFailedFile ) {
83+ let errorMessage = "Document ingestion failed. Please try again." ;
84+ if ( matchingTask . error ) {
85+ errorMessage = matchingTask . error ;
86+ } else {
87+ const failedFile = filesArray . find (
88+ ( file ) =>
89+ ( file . status === "failed" || file . status === "error" ) && file . error ,
90+ ) ;
91+ if ( failedFile ?. error ) {
92+ errorMessage = failedFile . error ;
93+ }
94+ }
95+
96+ setError ( errorMessage ) ;
97+ setCurrentStep ( null ) ;
98+ setUploadedTaskId ( null ) ;
99+ return ;
100+ }
101+
66102 // If task is completed or has processed files, complete the onboarding step
67103 if ( ! isTaskActive || ( matchingTask . processed_files ?? 0 ) > 0 ) {
68104 // Set to final step to show "Done"
@@ -169,6 +205,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
169205
170206 const performUpload = async ( file : File ) => {
171207 setIsUploading ( true ) ;
208+ setError ( null ) ;
172209 try {
173210 setCurrentStep ( 0 ) ;
174211 const result = await uploadFile ( file , true , true ) ; // Pass createFilter=true
@@ -193,6 +230,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
193230 const errorMessage =
194231 error instanceof Error ? error . message : "Upload failed" ;
195232 console . error ( "Upload failed" , errorMessage ) ;
233+ setError ( errorMessage ) ;
196234
197235 // Dispatch event that chat context can listen to
198236 // This avoids circular dependency issues
@@ -246,6 +284,25 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
246284 exit = { { opacity : 0 , y : - 24 } }
247285 transition = { { duration : 0.4 , ease : "easeInOut" } }
248286 >
287+ < AnimatePresence mode = "wait" >
288+ { error && (
289+ < motion . div
290+ key = "error"
291+ initial = { { opacity : 1 , y : 0 , height : "auto" } }
292+ exit = { { opacity : 0 , y : - 10 , height : 0 } }
293+ >
294+ < div className = "pb-6 flex items-center gap-4" >
295+ < X className = "w-4 h-4 text-destructive shrink-0" />
296+ < span
297+ data-testid = "onboarding-upload-error"
298+ className = "text-mmd text-muted-foreground"
299+ >
300+ { error }
301+ </ span >
302+ </ div >
303+ </ motion . div >
304+ ) }
305+ </ AnimatePresence >
249306 < Button
250307 size = "sm"
251308 variant = "outline"
0 commit comments