4040 "ErrorCodes" ,
4141]
4242
43+ _UNSET : Any = object ()
44+
4345
4446class ModuleError (Exception ):
4547 """Base error for all apcore framework errors."""
4648
49+ _default_retryable : bool | None = None
50+
4751 def __init__ (
4852 self ,
4953 code : str ,
5054 message : str ,
5155 details : dict [str , Any ] | None = None ,
5256 cause : Exception | None = None ,
5357 trace_id : str | None = None ,
58+ retryable : Any = _UNSET ,
59+ ai_guidance : str | None = None ,
60+ user_fixable : bool | None = None ,
61+ suggestion : str | None = None ,
5462 ) -> None :
5563 super ().__init__ (message )
5664 self .code = code
@@ -59,14 +67,43 @@ def __init__(
5967 self .cause = cause
6068 self .trace_id = trace_id
6169 self .timestamp = datetime .now (timezone .utc ).isoformat ()
70+ self .retryable = self ._default_retryable if retryable is _UNSET else retryable
71+ self .ai_guidance = ai_guidance
72+ self .user_fixable = user_fixable
73+ self .suggestion = suggestion
6274
6375 def __str__ (self ) -> str :
6476 return f"[{ self .code } ] { self .message } "
6577
78+ def to_dict (self ) -> dict [str , Any ]:
79+ """Serialize to dict with sparse output (null fields omitted)."""
80+ d : dict [str , Any ] = {
81+ "code" : self .code ,
82+ "message" : self .message ,
83+ }
84+ if self .details :
85+ d ["details" ] = self .details
86+ if self .cause is not None :
87+ d ["cause" ] = str (self .cause )
88+ if self .trace_id is not None :
89+ d ["trace_id" ] = self .trace_id
90+ d ["timestamp" ] = self .timestamp
91+ if self .retryable is not None :
92+ d ["retryable" ] = self .retryable
93+ if self .ai_guidance is not None :
94+ d ["ai_guidance" ] = self .ai_guidance
95+ if self .user_fixable is not None :
96+ d ["user_fixable" ] = self .user_fixable
97+ if self .suggestion is not None :
98+ d ["suggestion" ] = self .suggestion
99+ return d
100+
66101
67102class ConfigNotFoundError (ModuleError ):
68103 """Raised when a configuration file cannot be found."""
69104
105+ _default_retryable : bool | None = False
106+
70107 def __init__ (self , config_path : str , ** kwargs : Any ) -> None :
71108 super ().__init__ (
72109 code = "CONFIG_NOT_FOUND" ,
@@ -79,20 +116,26 @@ def __init__(self, config_path: str, **kwargs: Any) -> None:
79116class ConfigError (ModuleError ):
80117 """Raised when configuration is invalid."""
81118
119+ _default_retryable : bool | None = False
120+
82121 def __init__ (self , message : str , ** kwargs : Any ) -> None :
83122 super ().__init__ (code = "CONFIG_INVALID" , message = message , ** kwargs )
84123
85124
86125class ACLRuleError (ModuleError ):
87126 """Raised when an ACL rule is invalid."""
88127
128+ _default_retryable : bool | None = False
129+
89130 def __init__ (self , message : str , ** kwargs : Any ) -> None :
90131 super ().__init__ (code = "ACL_RULE_ERROR" , message = message , ** kwargs )
91132
92133
93134class ACLDeniedError (ModuleError ):
94135 """Raised when ACL denies access."""
95136
137+ _default_retryable : bool | None = False
138+
96139 def __init__ (self , caller_id : str | None , target_id : str , ** kwargs : Any ) -> None :
97140 super ().__init__ (
98141 code = "ACL_DENIED" ,
@@ -120,6 +163,8 @@ class ApprovalError(ModuleError):
120163 ``apcore.approval`` where ``ApprovalResult`` is defined.
121164 """
122165
166+ _default_retryable : bool | None = False
167+
123168 def __init__ (
124169 self ,
125170 code : str ,
@@ -149,6 +194,8 @@ def reason(self) -> str | None:
149194class ApprovalDeniedError (ApprovalError ):
150195 """Raised when an approval handler rejects the request."""
151196
197+ _default_retryable : bool | None = False
198+
152199 def __init__ (self , result : Any , module_id : str = "" , ** kwargs : Any ) -> None :
153200 reason = getattr (result , "reason" , None ) or ""
154201 msg = f"Approval denied for module '{ module_id } '"
@@ -166,6 +213,8 @@ def __init__(self, result: Any, module_id: str = "", **kwargs: Any) -> None:
166213class ApprovalTimeoutError (ApprovalError ):
167214 """Raised when an approval request times out."""
168215
216+ _default_retryable : bool | None = True
217+
169218 def __init__ (self , result : Any , module_id : str = "" , ** kwargs : Any ) -> None :
170219 super ().__init__ (
171220 code = "APPROVAL_TIMEOUT" ,
@@ -179,6 +228,8 @@ def __init__(self, result: Any, module_id: str = "", **kwargs: Any) -> None:
179228class ApprovalPendingError (ApprovalError ):
180229 """Raised when an approval is pending async resolution (Phase B)."""
181230
231+ _default_retryable : bool | None = False
232+
182233 def __init__ (self , result : Any , module_id : str = "" , ** kwargs : Any ) -> None :
183234 approval_id = getattr (result , "approval_id" , None )
184235 super ().__init__ (
@@ -199,6 +250,8 @@ def approval_id(self) -> str | None:
199250class ModuleNotFoundError (ModuleError ):
200251 """Raised when a module cannot be found."""
201252
253+ _default_retryable : bool | None = False
254+
202255 def __init__ (self , module_id : str , ** kwargs : Any ) -> None :
203256 super ().__init__ (
204257 code = "MODULE_NOT_FOUND" ,
@@ -211,6 +264,8 @@ def __init__(self, module_id: str, **kwargs: Any) -> None:
211264class ModuleTimeoutError (ModuleError ):
212265 """Raised when module execution exceeds timeout."""
213266
267+ _default_retryable : bool | None = True
268+
214269 def __init__ (self , module_id : str , timeout_ms : int , ** kwargs : Any ) -> None :
215270 super ().__init__ (
216271 code = "MODULE_TIMEOUT" ,
@@ -233,6 +288,8 @@ def timeout_ms(self) -> int:
233288class SchemaValidationError (ModuleError ):
234289 """Raised when schema validation fails."""
235290
291+ _default_retryable : bool | None = False
292+
236293 def __init__ (
237294 self ,
238295 message : str = "Schema validation failed" ,
@@ -250,6 +307,8 @@ def __init__(
250307class SchemaNotFoundError (ModuleError ):
251308 """Raised when a schema file or reference target cannot be found."""
252309
310+ _default_retryable : bool | None = False
311+
253312 def __init__ (self , schema_id : str , ** kwargs : Any ) -> None :
254313 super ().__init__ (
255314 code = "SCHEMA_NOT_FOUND" ,
@@ -262,13 +321,17 @@ def __init__(self, schema_id: str, **kwargs: Any) -> None:
262321class SchemaParseError (ModuleError ):
263322 """Raised when a schema file has invalid syntax."""
264323
324+ _default_retryable : bool | None = False
325+
265326 def __init__ (self , message : str , ** kwargs : Any ) -> None :
266327 super ().__init__ (code = "SCHEMA_PARSE_ERROR" , message = message , ** kwargs )
267328
268329
269330class SchemaCircularRefError (ModuleError ):
270331 """Raised when circular $ref references are detected."""
271332
333+ _default_retryable : bool | None = False
334+
272335 def __init__ (self , ref_path : str , ** kwargs : Any ) -> None :
273336 super ().__init__ (
274337 code = "SCHEMA_CIRCULAR_REF" ,
@@ -281,6 +344,8 @@ def __init__(self, ref_path: str, **kwargs: Any) -> None:
281344class CallDepthExceededError (ModuleError ):
282345 """Raised when call chain exceeds maximum depth."""
283346
347+ _default_retryable : bool | None = False
348+
284349 def __init__ (self , depth : int , max_depth : int , call_chain : list [str ], ** kwargs : Any ) -> None :
285350 super ().__init__ (
286351 code = "CALL_DEPTH_EXCEEDED" ,
@@ -303,6 +368,8 @@ def max_depth(self) -> int:
303368class CircularCallError (ModuleError ):
304369 """Raised when a circular call is detected."""
305370
371+ _default_retryable : bool | None = False
372+
306373 def __init__ (self , module_id : str , call_chain : list [str ], ** kwargs : Any ) -> None :
307374 super ().__init__ (
308375 code = "CIRCULAR_CALL" ,
@@ -320,6 +387,8 @@ def module_id(self) -> str:
320387class CallFrequencyExceededError (ModuleError ):
321388 """Raised when a module is called too many times."""
322389
390+ _default_retryable : bool | None = False
391+
323392 def __init__ (
324393 self ,
325394 module_id : str ,
@@ -359,13 +428,17 @@ def max_repeat(self) -> int:
359428class InvalidInputError (ModuleError ):
360429 """Raised for invalid input."""
361430
431+ _default_retryable : bool | None = False
432+
362433 def __init__ (self , message : str = "Invalid input" , ** kwargs : Any ) -> None :
363434 super ().__init__ (code = "GENERAL_INVALID_INPUT" , message = message , ** kwargs )
364435
365436
366437class FuncMissingTypeHintError (ModuleError ):
367438 """Raised when a function parameter has no type annotation or a forward reference cannot be resolved."""
368439
440+ _default_retryable : bool | None = False
441+
369442 def __init__ (self , * , function_name : str , parameter_name : str , ** kwargs : Any ) -> None :
370443 super ().__init__ (
371444 code = "FUNC_MISSING_TYPE_HINT" ,
@@ -381,6 +454,8 @@ def __init__(self, *, function_name: str, parameter_name: str, **kwargs: Any) ->
381454class FuncMissingReturnTypeError (ModuleError ):
382455 """Raised when a function has no return type annotation."""
383456
457+ _default_retryable : bool | None = False
458+
384459 def __init__ (self , * , function_name : str , ** kwargs : Any ) -> None :
385460 super ().__init__ (
386461 code = "FUNC_MISSING_RETURN_TYPE" ,
@@ -393,6 +468,8 @@ def __init__(self, *, function_name: str, **kwargs: Any) -> None:
393468class BindingInvalidTargetError (ModuleError ):
394469 """Raised when a binding target string does not contain a ':' separator."""
395470
471+ _default_retryable : bool | None = False
472+
396473 def __init__ (self , * , target : str , ** kwargs : Any ) -> None :
397474 super ().__init__ (
398475 code = "BINDING_INVALID_TARGET" ,
@@ -405,6 +482,8 @@ def __init__(self, *, target: str, **kwargs: Any) -> None:
405482class BindingModuleNotFoundError (ModuleError ):
406483 """Raised when a binding target module cannot be imported."""
407484
485+ _default_retryable : bool | None = False
486+
408487 def __init__ (self , * , module_path : str , ** kwargs : Any ) -> None :
409488 super ().__init__ (
410489 code = "BINDING_MODULE_NOT_FOUND" ,
@@ -417,6 +496,8 @@ def __init__(self, *, module_path: str, **kwargs: Any) -> None:
417496class BindingCallableNotFoundError (ModuleError ):
418497 """Raised when a callable cannot be found in the target module."""
419498
499+ _default_retryable : bool | None = False
500+
420501 def __init__ (self , * , callable_name : str , module_path : str , ** kwargs : Any ) -> None :
421502 super ().__init__ (
422503 code = "BINDING_CALLABLE_NOT_FOUND" ,
@@ -429,6 +510,8 @@ def __init__(self, *, callable_name: str, module_path: str, **kwargs: Any) -> No
429510class BindingNotCallableError (ModuleError ):
430511 """Raised when a resolved binding target is not callable."""
431512
513+ _default_retryable : bool | None = False
514+
432515 def __init__ (self , * , target : str , ** kwargs : Any ) -> None :
433516 super ().__init__ (
434517 code = "BINDING_NOT_CALLABLE" ,
@@ -441,6 +524,8 @@ def __init__(self, *, target: str, **kwargs: Any) -> None:
441524class BindingSchemaMissingError (ModuleError ):
442525 """Raised when no schema is provided and auto-generation from type hints fails."""
443526
527+ _default_retryable : bool | None = False
528+
444529 def __init__ (self , * , target : str , ** kwargs : Any ) -> None :
445530 super ().__init__ (
446531 code = "BINDING_SCHEMA_MISSING" ,
@@ -453,6 +538,8 @@ def __init__(self, *, target: str, **kwargs: Any) -> None:
453538class BindingFileInvalidError (ModuleError ):
454539 """Raised when a binding file has parse errors, missing required fields, or is empty."""
455540
541+ _default_retryable : bool | None = False
542+
456543 def __init__ (self , * , file_path : str , reason : str , ** kwargs : Any ) -> None :
457544 super ().__init__ (
458545 code = "BINDING_FILE_INVALID" ,
@@ -465,6 +552,8 @@ def __init__(self, *, file_path: str, reason: str, **kwargs: Any) -> None:
465552class CircularDependencyError (ModuleError ):
466553 """Raised when circular dependencies are detected among modules."""
467554
555+ _default_retryable : bool | None = False
556+
468557 def __init__ (self , cycle_path : list [str ], ** kwargs : Any ) -> None :
469558 super ().__init__ (
470559 code = "CIRCULAR_DEPENDENCY" ,
@@ -477,6 +566,8 @@ def __init__(self, cycle_path: list[str], **kwargs: Any) -> None:
477566class ModuleLoadError (ModuleError ):
478567 """Raised when a module file cannot be loaded or resolved."""
479568
569+ _default_retryable : bool | None = False
570+
480571 def __init__ (self , module_id : str , reason : str , ** kwargs : Any ) -> None :
481572 super ().__init__ (
482573 code = "MODULE_LOAD_ERROR" ,
@@ -489,6 +580,8 @@ def __init__(self, module_id: str, reason: str, **kwargs: Any) -> None:
489580class ModuleExecuteError (ModuleError ):
490581 """Raised when module execution fails with an unhandled error."""
491582
583+ _default_retryable : bool | None = None
584+
492585 def __init__ (self , module_id : str = "" , message : str = "Module execution failed" , ** kwargs : Any ) -> None :
493586 super ().__init__ (
494587 code = "MODULE_EXECUTE_ERROR" ,
@@ -501,6 +594,8 @@ def __init__(self, module_id: str = "", message: str = "Module execution failed"
501594class InternalError (ModuleError ):
502595 """Raised for unexpected internal framework errors."""
503596
597+ _default_retryable : bool | None = True
598+
504599 def __init__ (self , message : str = "Internal error" , ** kwargs : Any ) -> None :
505600 super ().__init__ (
506601 code = "GENERAL_INTERNAL_ERROR" ,
0 commit comments