@@ -102,6 +102,58 @@ async def _await_if_needed(value: Any) -> Any:
102102 return value
103103
104104
105+ def _decorator_target_name (instance : Any , method_name : str | None = None ) -> str :
106+ class_name = instance .__class__ .__name__
107+ if method_name is None :
108+ return class_name
109+ return f"{ class_name } .{ method_name } "
110+
111+
112+ def _decorator_error (
113+ * ,
114+ instance : Any ,
115+ decorator_name : str ,
116+ exc : Exception ,
117+ method_name : str | None = None ,
118+ details : str | None = None ,
119+ ) -> RuntimeError :
120+ message = f"{ _decorator_target_name (instance , method_name )} { decorator_name } failed"
121+ if details :
122+ message += f" ({ details } )"
123+ message += f": { exc } "
124+ return RuntimeError (message )
125+
126+
127+ def _http_api_details (meta : HttpApiMeta ) -> str :
128+ details = [f"route={ meta .route !r} " , f"methods={ list (meta .methods )!r} " ]
129+ if meta .capability_name :
130+ details .append (f"capability_name={ meta .capability_name !r} " )
131+ return ", " .join (details )
132+
133+
134+ def _provider_change_details (meta : Any ) -> str :
135+ return f"provider_types={ list (meta .provider_types )!r} "
136+
137+
138+ def _background_task_details (meta : BackgroundTaskMeta , method_name : str ) -> str :
139+ description = meta .description or f"background_task:{ method_name } "
140+ return (
141+ f"description={ description !r} , auto_start={ meta .auto_start !r} , "
142+ f"on_error={ meta .on_error !r} "
143+ )
144+
145+
146+ def _mcp_server_details (meta : MCPServerMeta ) -> str :
147+ return (
148+ f"name={ meta .name !r} , scope={ meta .scope !r} , timeout={ meta .timeout !r} , "
149+ f"wait_until_ready={ meta .wait_until_ready !r} "
150+ )
151+
152+
153+ def _skill_details (name : str , path : str ) -> str :
154+ return f"name={ name !r} , path={ path !r} "
155+
156+
105157def _normalize_provider_type (value : Any ) -> str :
106158 enum_value = getattr (value , "value" , None )
107159 if isinstance (enum_value , str ):
@@ -130,9 +182,7 @@ async def _run_model_validation(
130182 try :
131183 validated = meta .model .model_validate (config )
132184 except ValidationError as exc :
133- raise ValueError (
134- f"{ instance .__class__ .__name__ } .{ method_name } validate_config failed: { exc } "
135- ) from exc
185+ raise ValueError (str (exc )) from exc
136186 _validated_config_store (instance )[method_name ] = validated
137187 return
138188
@@ -207,21 +257,38 @@ async def _run_validate_config(instance: Any, context: RuntimeContext) -> None:
207257 meta = get_validate_config_meta (raw )
208258 if meta is None :
209259 continue
210- await _run_model_validation (
211- instance = instance ,
212- method_name = method_name ,
213- meta = meta ,
214- config = config ,
215- )
260+ try :
261+ await _run_model_validation (
262+ instance = instance ,
263+ method_name = method_name ,
264+ meta = meta ,
265+ config = config ,
266+ )
267+ except Exception as exc :
268+ raise _decorator_error (
269+ instance = instance ,
270+ method_name = method_name ,
271+ decorator_name = "@validate_config" ,
272+ exc = exc ,
273+ ) from exc
216274
217275
218276async def _register_http_apis (instance : Any , context : RuntimeContext ) -> None :
219277 state = _runtime_state (instance )
220- for _method_name , bound , raw in _iter_bound_methods (instance ):
278+ for method_name , bound , raw in _iter_bound_methods (instance ):
221279 meta = get_http_api_meta (raw )
222280 if meta is None :
223281 continue
224- await _register_http_api (bound = bound , meta = meta , context = context )
282+ try :
283+ await _register_http_api (bound = bound , meta = meta , context = context )
284+ except Exception as exc :
285+ raise _decorator_error (
286+ instance = instance ,
287+ method_name = method_name ,
288+ decorator_name = "@http_api" ,
289+ details = _http_api_details (meta ),
290+ exc = exc ,
291+ ) from exc
225292 state .http_apis .append ((meta .route , list (meta .methods )))
226293
227294
@@ -252,10 +319,11 @@ async def _register_provider_change_hooks(
252319 context : RuntimeContext ,
253320) -> None :
254321 state = _runtime_state (instance )
255- for _method_name , bound , raw in _iter_bound_methods (instance ):
322+ for method_name , bound , raw in _iter_bound_methods (instance ):
256323 meta = get_provider_change_meta (raw )
257324 if meta is None :
258325 continue
326+ target_name = _decorator_target_name (instance , method_name )
259327
260328 async def callback (
261329 provider_id : str ,
@@ -270,11 +338,29 @@ async def callback(
270338 if current_type not in _meta .provider_types :
271339 return
272340 owner = instance if isinstance (instance , Star ) else None
273- with bind_star_runtime (owner , context ):
274- result = _bound (provider_id , provider_type , umo )
275- await _await_if_needed (result )
341+ try :
342+ with bind_star_runtime (owner , context ):
343+ result = _bound (provider_id , provider_type , umo )
344+ await _await_if_needed (result )
345+ except Exception as exc :
346+ raise RuntimeError (
347+ f"{ target_name } @on_provider_change callback failed "
348+ f"(provider_id={ provider_id !r} , provider_type={ provider_type !r} , "
349+ f"umo={ umo !r} ): { exc } "
350+ ) from exc
276351
277- task = await context .provider_manager .register_provider_change_hook (callback )
352+ try :
353+ task = await context .provider_manager .register_provider_change_hook (
354+ callback
355+ )
356+ except Exception as exc :
357+ raise _decorator_error (
358+ instance = instance ,
359+ method_name = method_name ,
360+ decorator_name = "@on_provider_change" ,
361+ details = _provider_change_details (meta ),
362+ exc = exc ,
363+ ) from exc
278364 # TODO: provider.manager.watch_changes is currently restricted to
279365 # reserved/system plugins. If this decorator should be public-facing,
280366 # the capability boundary needs to be widened or a dedicated event feed
@@ -288,17 +374,26 @@ async def _start_background_tasks(instance: Any, context: RuntimeContext) -> Non
288374 meta = get_background_task_meta (raw )
289375 if meta is None or not meta .auto_start :
290376 continue
291- task = await context .register_task (
292- _background_runner (
377+ try :
378+ task = await context .register_task (
379+ _background_runner (
380+ instance = instance ,
381+ bound = bound ,
382+ context = context ,
383+ meta = meta ,
384+ method_name = method_name ,
385+ ),
386+ meta .description
387+ or f"background_task:{ instance .__class__ .__name__ } .{ method_name } " ,
388+ )
389+ except Exception as exc :
390+ raise _decorator_error (
293391 instance = instance ,
294- bound = bound ,
295- context = context ,
296- meta = meta ,
297392 method_name = method_name ,
298- ) ,
299- meta . description
300- or f"background_task: { instance . __class__ . __name__ } . { method_name } " ,
301- )
393+ decorator_name = "@background_task" ,
394+ details = _background_task_details ( meta , method_name ),
395+ exc = exc ,
396+ ) from exc
302397 state .background_tasks .append (task )
303398
304399
@@ -319,41 +414,68 @@ async def _background_runner(
319414 return
320415 except asyncio .CancelledError :
321416 raise
322- except Exception :
417+ except Exception as exc :
323418 if meta .on_error != "restart" :
324- raise
419+ raise _decorator_error (
420+ instance = instance ,
421+ method_name = method_name ,
422+ decorator_name = "@background_task" ,
423+ details = _background_task_details (meta , method_name ),
424+ exc = exc ,
425+ ) from exc
325426 context .logger .exception (
326- "SDK decorator background_task restarting after failure: plugin_id={} task={}" ,
427+ "SDK decorator background_task restarting after failure: plugin_id={} task={} details={} " ,
327428 context .plugin_id ,
328429 f"{ instance .__class__ .__name__ } .{ method_name } " ,
430+ _background_task_details (meta , method_name ),
329431 )
330432
331433
332- def _iter_class_and_method_meta (
434+ def _iter_class_and_method_meta_entries (
333435 instance : Any ,
334436 getter ,
335- ) -> list [Any ]:
336- values = list (getter (instance .__class__ ))
337- for _method_name , _bound , raw in _iter_bound_methods (instance ):
338- values .extend (getter (raw ))
437+ ) -> list [tuple [str , Any ]]:
438+ values = [
439+ (_decorator_target_name (instance ), meta ) for meta in getter (instance .__class__ )
440+ ]
441+ for method_name , _bound , raw in _iter_bound_methods (instance ):
442+ values .extend (
443+ (_decorator_target_name (instance , method_name ), meta )
444+ for meta in getter (raw )
445+ )
339446 return values
340447
341448
342449async def _register_skills (instance : Any , context : RuntimeContext ) -> None :
343450 state = _runtime_state (instance )
344- for meta in _iter_class_and_method_meta (instance , get_skill_meta ):
345- await context .register_skill (
346- name = meta .name ,
347- path = meta .path ,
348- description = meta .description ,
349- )
451+ for target_name , meta in _iter_class_and_method_meta_entries (
452+ instance , get_skill_meta
453+ ):
454+ try :
455+ await context .register_skill (
456+ name = meta .name ,
457+ path = meta .path ,
458+ description = meta .description ,
459+ )
460+ except Exception as exc :
461+ raise RuntimeError (
462+ f"{ target_name } @register_skill failed "
463+ f"({ _skill_details (meta .name , meta .path )} ): { exc } "
464+ ) from exc
350465 state .registered_skills .append (meta .name )
351466
352467
353468async def _register_mcp_servers (instance : Any , context : RuntimeContext ) -> None :
354469 state = _runtime_state (instance )
355- for meta in _iter_class_and_method_meta (instance , get_mcp_server_meta ):
356- await _register_mcp_server (meta = meta , context = context )
470+ for target_name , meta in _iter_class_and_method_meta_entries (
471+ instance , get_mcp_server_meta
472+ ):
473+ try :
474+ await _register_mcp_server (meta = meta , context = context )
475+ except Exception as exc :
476+ raise RuntimeError (
477+ f"{ target_name } @mcp_server failed ({ _mcp_server_details (meta )} ): { exc } "
478+ ) from exc
357479 if meta .scope == "global" :
358480 state .global_mcp_servers .append (meta .name )
359481 else :
@@ -453,6 +575,8 @@ async def run_lifecycle_with_decorators(
453575 method_name : str ,
454576 context : RuntimeContext ,
455577) -> None :
578+ # Wrap decorator-managed startup failures with decorator-specific context so
579+ # plugin authors do not only see a generic worker initialize timeout.
456580 # Keep the lifecycle wrapper centralized so decorator-managed resources still
457581 # work when plugins override on_start/on_stop without calling super().
458582 if method_name == "on_start" :
0 commit comments