xref: /aosp_15_r20/external/tensorflow/tensorflow/python/framework/errors_impl.py (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Exception types for TensorFlow errors."""
16
17import traceback
18import warnings
19
20from tensorflow.core.lib.core import error_codes_pb2
21from tensorflow.python import _pywrap_py_exception_registry
22from tensorflow.python.client import pywrap_tf_session as c_api
23from tensorflow.python.framework import c_api_util
24from tensorflow.python.util import compat
25from tensorflow.python.util import deprecation
26from tensorflow.python.util import tf_inspect
27from tensorflow.python.util.tf_export import tf_export
28
29
30class InaccessibleTensorError(ValueError):
31  pass
32
33
34@tf_export("errors.OperatorNotAllowedInGraphError", v1=[])
35class OperatorNotAllowedInGraphError(TypeError):
36  """An error is raised for unsupported operator in Graph execution.
37
38  For example, using a `tf.Tensor` as a Python `bool` in Graph execution
39  is not allowed.
40  """
41  pass
42
43
44@tf_export("errors.OpError", v1=["errors.OpError", "OpError"])
45@deprecation.deprecated_endpoints("OpError")
46class OpError(Exception):
47  """The base class for TensorFlow exceptions.
48
49  Usually, TensorFlow will raise a more specific subclass of `OpError` from the
50  `tf.errors` module.
51  """
52
53  def __init__(self, node_def, op, message, error_code, *args):
54    """Creates a new `OpError` indicating that a particular op failed.
55
56    Args:
57      node_def: The `node_def_pb2.NodeDef` proto representing the op that
58        failed, if known; otherwise None.
59      op: The `ops.Operation` that failed, if known; otherwise None. During
60        eager execution, this field is always `None`.
61      message: The message string describing the failure.
62      error_code: The `error_codes_pb2.Code` describing the error.
63      *args: If not empty, it should contain a dictionary describing details
64        about the error. This argument is inspired by Abseil payloads:
65        https://github.com/abseil/abseil-cpp/blob/master/absl/status/status.h
66    """
67    super(OpError, self).__init__()
68    self._node_def = node_def
69    self._op = op
70    self._message = message
71    self._error_code = error_code
72    if args:
73      self._experimental_payloads = args[0]
74    else:
75      self._experimental_payloads = {}
76
77  def __reduce__(self):
78    # Allow the subclasses to accept less arguments in their __init__.
79    init_argspec = tf_inspect.getargspec(self.__class__.__init__)
80    args = tuple(getattr(self, arg) for arg in init_argspec.args[1:])
81    return self.__class__, args
82
83  @property
84  def message(self):
85    """The error message that describes the error."""
86    return self._message
87
88  @property
89  def op(self):
90    """The operation that failed, if known.
91
92    *N.B.* If the failed op was synthesized at runtime, e.g. a `Send`
93    or `Recv` op, there will be no corresponding
94    `tf.Operation`
95    object.  In that case, this will return `None`, and you should
96    instead use the `tf.errors.OpError.node_def` to
97    discover information about the op.
98
99    Returns:
100      The `Operation` that failed, or None.
101    """
102    return self._op
103
104  @property
105  def error_code(self):
106    """The integer error code that describes the error."""
107    return self._error_code
108
109  @property
110  def node_def(self):
111    """The `NodeDef` proto representing the op that failed."""
112    return self._node_def
113
114  @property
115  def experimental_payloads(self):
116    """A dictionary describing the details of the error."""
117    return self._experimental_payloads
118
119  def __str__(self):
120    if self._op is not None:
121      output = [
122          "%s\n\nOriginal stack trace for %r:\n" % (
123              self.message,
124              self._op.name,
125          )
126      ]
127      curr_traceback_list = traceback.format_list(self._op.traceback or [])
128      output.extend(curr_traceback_list)
129      # pylint: disable=protected-access
130      original_op = self._op._original_op
131      # pylint: enable=protected-access
132      while original_op is not None:
133        output.append(
134            "\n...which was originally created as op %r, defined at:\n" %
135            (original_op.name,))
136        prev_traceback_list = curr_traceback_list
137        curr_traceback_list = traceback.format_list(original_op.traceback or [])
138
139        # Attempt to elide large common subsequences of the subsequent
140        # stack traces.
141        #
142        # TODO(mrry): Consider computing the actual longest common subsequence.
143        is_eliding = False
144        elide_count = 0
145        last_elided_line = None
146        for line, line_in_prev in zip(curr_traceback_list, prev_traceback_list):
147          if line == line_in_prev:
148            if is_eliding:
149              elide_count += 1
150              last_elided_line = line
151            else:
152              output.append(line)
153              is_eliding = True
154              elide_count = 0
155          else:
156            if is_eliding:
157              if elide_count > 0:
158                output.extend([
159                    "[elided %d identical lines from previous traceback]\n" %
160                    (elide_count - 1,), last_elided_line
161                ])
162              is_eliding = False
163            output.extend(line)
164
165        # pylint: disable=protected-access
166        original_op = original_op._original_op
167        # pylint: enable=protected-access
168      return "".join(output)
169    else:
170      return self.message
171
172
173OK = error_codes_pb2.OK
174tf_export("errors.OK").export_constant(__name__, "OK")
175CANCELLED = error_codes_pb2.CANCELLED
176tf_export("errors.CANCELLED").export_constant(__name__, "CANCELLED")
177UNKNOWN = error_codes_pb2.UNKNOWN
178tf_export("errors.UNKNOWN").export_constant(__name__, "UNKNOWN")
179INVALID_ARGUMENT = error_codes_pb2.INVALID_ARGUMENT
180tf_export("errors.INVALID_ARGUMENT").export_constant(__name__,
181                                                     "INVALID_ARGUMENT")
182DEADLINE_EXCEEDED = error_codes_pb2.DEADLINE_EXCEEDED
183tf_export("errors.DEADLINE_EXCEEDED").export_constant(__name__,
184                                                      "DEADLINE_EXCEEDED")
185NOT_FOUND = error_codes_pb2.NOT_FOUND
186tf_export("errors.NOT_FOUND").export_constant(__name__, "NOT_FOUND")
187ALREADY_EXISTS = error_codes_pb2.ALREADY_EXISTS
188tf_export("errors.ALREADY_EXISTS").export_constant(__name__, "ALREADY_EXISTS")
189PERMISSION_DENIED = error_codes_pb2.PERMISSION_DENIED
190tf_export("errors.PERMISSION_DENIED").export_constant(__name__,
191                                                      "PERMISSION_DENIED")
192UNAUTHENTICATED = error_codes_pb2.UNAUTHENTICATED
193tf_export("errors.UNAUTHENTICATED").export_constant(__name__, "UNAUTHENTICATED")
194RESOURCE_EXHAUSTED = error_codes_pb2.RESOURCE_EXHAUSTED
195tf_export("errors.RESOURCE_EXHAUSTED").export_constant(__name__,
196                                                       "RESOURCE_EXHAUSTED")
197FAILED_PRECONDITION = error_codes_pb2.FAILED_PRECONDITION
198tf_export("errors.FAILED_PRECONDITION").export_constant(__name__,
199                                                        "FAILED_PRECONDITION")
200ABORTED = error_codes_pb2.ABORTED
201tf_export("errors.ABORTED").export_constant(__name__, "ABORTED")
202OUT_OF_RANGE = error_codes_pb2.OUT_OF_RANGE
203tf_export("errors.OUT_OF_RANGE").export_constant(__name__, "OUT_OF_RANGE")
204UNIMPLEMENTED = error_codes_pb2.UNIMPLEMENTED
205tf_export("errors.UNIMPLEMENTED").export_constant(__name__, "UNIMPLEMENTED")
206INTERNAL = error_codes_pb2.INTERNAL
207tf_export("errors.INTERNAL").export_constant(__name__, "INTERNAL")
208UNAVAILABLE = error_codes_pb2.UNAVAILABLE
209tf_export("errors.UNAVAILABLE").export_constant(__name__, "UNAVAILABLE")
210DATA_LOSS = error_codes_pb2.DATA_LOSS
211tf_export("errors.DATA_LOSS").export_constant(__name__, "DATA_LOSS")
212
213
214# pylint: disable=line-too-long
215@tf_export("errors.CancelledError")
216class CancelledError(OpError):
217  """Raised when an operation or step is cancelled.
218
219  For example, a long-running operation (e.g.
220  `tf.QueueBase.enqueue` may be
221  cancelled by running another operation (e.g.
222  `tf.QueueBase.close`,
223  or by `tf.Session.close`.
224  A step that is running such a long-running operation will fail by raising
225  `CancelledError`.
226
227  @@__init__
228  """
229
230  def __init__(self, node_def, op, message, *args):
231    """Creates a `CancelledError`."""
232    super(CancelledError, self).__init__(node_def, op, message, CANCELLED,
233                                         *args)
234
235
236# pylint: enable=line-too-long
237
238
239@tf_export("errors.UnknownError")
240class UnknownError(OpError):
241  """Unknown error.
242
243  An example of where this error may be returned is if a Status value
244  received from another address space belongs to an error-space that
245  is not known to this address space. Also, errors raised by APIs that
246  do not return enough error information may be converted to this
247  error.
248
249  @@__init__
250  """
251
252  def __init__(self, node_def, op, message, *args):
253    """Creates an `UnknownError`."""
254    super(UnknownError, self).__init__(node_def, op, message, UNKNOWN, *args)
255
256
257@tf_export("errors.InvalidArgumentError")
258class InvalidArgumentError(OpError):
259  """Raised when an operation receives an invalid argument.
260
261  This error is typically raised when an op receives mismatched arguments.
262
263  Example:
264
265  >>> tf.reshape([1, 2, 3], (2,))
266  Traceback (most recent call last):
267     ...
268  InvalidArgumentError: ...
269
270  @@__init__
271  """
272
273  def __init__(self, node_def, op, message, *args):
274    """Creates an `InvalidArgumentError`."""
275    super(InvalidArgumentError, self).__init__(node_def, op, message,
276                                               INVALID_ARGUMENT, *args)
277
278
279@tf_export("errors.DeadlineExceededError")
280class DeadlineExceededError(OpError):
281  """Raised when a deadline expires before an operation could complete.
282
283  This exception is not currently used.
284
285  @@__init__
286  """
287
288  def __init__(self, node_def, op, message, *args):
289    """Creates a `DeadlineExceededError`."""
290    super(DeadlineExceededError, self).__init__(node_def, op, message,
291                                                DEADLINE_EXCEEDED, *args)
292
293
294@tf_export("errors.NotFoundError")
295class NotFoundError(OpError):
296  """Raised when a requested entity (e.g., a file or directory) was not found.
297
298  For example, running the
299  `tf.WholeFileReader.read`
300  operation could raise `NotFoundError` if it receives the name of a file that
301  does not exist.
302
303  @@__init__
304  """
305
306  def __init__(self, node_def, op, message, *args):
307    """Creates a `NotFoundError`."""
308    super(NotFoundError, self).__init__(node_def, op, message, NOT_FOUND, *args)
309
310
311@tf_export("errors.AlreadyExistsError")
312class AlreadyExistsError(OpError):
313  """Raised when an entity that we attempted to create already exists.
314
315  For example, running an operation that saves a file
316  (e.g. `tf.train.Saver.save`)
317  could potentially raise this exception if an explicit filename for an
318  existing file was passed.
319
320  @@__init__
321  """
322
323  def __init__(self, node_def, op, message, *args):
324    """Creates an `AlreadyExistsError`."""
325    super(AlreadyExistsError, self).__init__(node_def, op, message,
326                                             ALREADY_EXISTS, *args)
327
328
329@tf_export("errors.PermissionDeniedError")
330class PermissionDeniedError(OpError):
331  """Raised when the caller does not have permission to run an operation.
332
333  For example, running the
334  `tf.WholeFileReader.read`
335  operation could raise `PermissionDeniedError` if it receives the name of a
336  file for which the user does not have the read file permission.
337
338  @@__init__
339  """
340
341  def __init__(self, node_def, op, message, *args):
342    """Creates a `PermissionDeniedError`."""
343    super(PermissionDeniedError, self).__init__(node_def, op, message,
344                                                PERMISSION_DENIED, *args)
345
346
347@tf_export("errors.UnauthenticatedError")
348class UnauthenticatedError(OpError):
349  """The request does not have valid authentication credentials.
350
351  This exception is not currently used.
352
353  @@__init__
354  """
355
356  def __init__(self, node_def, op, message, *args):
357    """Creates an `UnauthenticatedError`."""
358    super(UnauthenticatedError, self).__init__(node_def, op, message,
359                                               UNAUTHENTICATED, *args)
360
361
362@tf_export("errors.ResourceExhaustedError")
363class ResourceExhaustedError(OpError):
364  """Some resource has been exhausted.
365
366  For example, this error might be raised if a per-user quota is
367  exhausted, or perhaps the entire file system is out of space.
368
369  @@__init__
370  """
371
372  def __init__(self, node_def, op, message, *args):
373    """Creates a `ResourceExhaustedError`."""
374    super(ResourceExhaustedError, self).__init__(node_def, op, message,
375                                                 RESOURCE_EXHAUSTED, *args)
376
377
378@tf_export("errors.FailedPreconditionError")
379class FailedPreconditionError(OpError):
380  """Operation was rejected because the system is not in a state to execute it.
381
382  This exception is most commonly raised when running an operation
383  that reads a `tf.Variable`
384  before it has been initialized.
385
386  @@__init__
387  """
388
389  def __init__(self, node_def, op, message, *args):
390    """Creates a `FailedPreconditionError`."""
391    super(FailedPreconditionError, self).__init__(node_def, op, message,
392                                                  FAILED_PRECONDITION, *args)
393
394
395@tf_export("errors.AbortedError")
396class AbortedError(OpError):
397  """The operation was aborted, typically due to a concurrent action.
398
399  For example, running a
400  `tf.QueueBase.enqueue`
401  operation may raise `AbortedError` if a
402  `tf.QueueBase.close` operation
403  previously ran.
404
405  @@__init__
406  """
407
408  def __init__(self, node_def, op, message, *args):
409    """Creates an `AbortedError`."""
410    super(AbortedError, self).__init__(node_def, op, message, ABORTED, *args)
411
412
413@tf_export("errors.OutOfRangeError")
414class OutOfRangeError(OpError):
415  """Raised when an operation iterates past the valid input range.
416
417  This exception is raised in "end-of-file" conditions, such as when a
418  `tf.QueueBase.dequeue`
419  operation is blocked on an empty queue, and a
420  `tf.QueueBase.close`
421  operation executes.
422
423  @@__init__
424  """
425
426  def __init__(self, node_def, op, message, *args):
427    """Creates an `OutOfRangeError`."""
428    super(OutOfRangeError, self).__init__(node_def, op, message, OUT_OF_RANGE,
429                                          *args)
430
431
432@tf_export("errors.UnimplementedError")
433class UnimplementedError(OpError):
434  """Raised when an operation has not been implemented.
435
436  Some operations may raise this error when passed otherwise-valid
437  arguments that it does not currently support. For example, running
438  the `tf.nn.max_pool2d` operation
439  would raise this error if pooling was requested on the batch dimension,
440  because this is not yet supported.
441
442  @@__init__
443  """
444
445  def __init__(self, node_def, op, message, *args):
446    """Creates an `UnimplementedError`."""
447    super(UnimplementedError, self).__init__(node_def, op, message,
448                                             UNIMPLEMENTED, *args)
449
450
451@tf_export("errors.InternalError")
452class InternalError(OpError):
453  """Raised when the system experiences an internal error.
454
455  This exception is raised when some invariant expected by the runtime
456  has been broken. Catching this exception is not recommended.
457
458  @@__init__
459  """
460
461  def __init__(self, node_def, op, message, *args):
462    """Creates an `InternalError`."""
463    super(InternalError, self).__init__(node_def, op, message, INTERNAL, *args)
464
465
466@tf_export("errors.UnavailableError")
467class UnavailableError(OpError):
468  """Raised when the runtime is currently unavailable.
469
470  This exception is not currently used.
471
472  @@__init__
473  """
474
475  def __init__(self, node_def, op, message, *args):
476    """Creates an `UnavailableError`."""
477    super(UnavailableError, self).__init__(node_def, op, message, UNAVAILABLE,
478                                           *args)
479
480
481@tf_export("errors.DataLossError")
482class DataLossError(OpError):
483  """Raised when unrecoverable data loss or corruption is encountered.
484
485  For example, this may be raised by running a
486  `tf.WholeFileReader.read`
487  operation, if the file is truncated while it is being read.
488
489  @@__init__
490  """
491
492  def __init__(self, node_def, op, message, *args):
493    """Creates a `DataLossError`."""
494    super(DataLossError, self).__init__(node_def, op, message, DATA_LOSS, *args)
495
496
497_CODE_TO_EXCEPTION_CLASS = {
498    CANCELLED: CancelledError,
499    UNKNOWN: UnknownError,
500    INVALID_ARGUMENT: InvalidArgumentError,
501    DEADLINE_EXCEEDED: DeadlineExceededError,
502    NOT_FOUND: NotFoundError,
503    ALREADY_EXISTS: AlreadyExistsError,
504    PERMISSION_DENIED: PermissionDeniedError,
505    UNAUTHENTICATED: UnauthenticatedError,
506    RESOURCE_EXHAUSTED: ResourceExhaustedError,
507    FAILED_PRECONDITION: FailedPreconditionError,
508    ABORTED: AbortedError,
509    OUT_OF_RANGE: OutOfRangeError,
510    UNIMPLEMENTED: UnimplementedError,
511    INTERNAL: InternalError,
512    UNAVAILABLE: UnavailableError,
513    DATA_LOSS: DataLossError,
514}
515
516_pywrap_py_exception_registry.PyExceptionRegistry_Init(_CODE_TO_EXCEPTION_CLASS)
517
518_EXCEPTION_CLASS_TO_CODE = {
519    class_: code for code, class_ in _CODE_TO_EXCEPTION_CLASS.items()
520}
521
522
523@tf_export(v1=["errors.exception_type_from_error_code"])
524def exception_type_from_error_code(error_code):
525  return _CODE_TO_EXCEPTION_CLASS[error_code]
526
527
528@tf_export(v1=["errors.error_code_from_exception_type"])
529def error_code_from_exception_type(cls):
530  try:
531    return _EXCEPTION_CLASS_TO_CODE[cls]
532  except KeyError:
533    warnings.warn("Unknown class exception")
534    return UnknownError(None, None, "Unknown class exception", None)
535
536
537def _make_specific_exception(node_def, op, message, error_code):
538  try:
539    exc_type = exception_type_from_error_code(error_code)
540    return exc_type(node_def, op, message)
541  except KeyError:
542    warnings.warn("Unknown error code: %d" % error_code)
543    return UnknownError(node_def, op, message, error_code)
544
545
546# Named like a function for backwards compatibility with the
547# @tf_contextlib.contextmanager version, which was switched to a class to avoid
548# some object creation overhead.
549# TODO(b/77295559): expand use of TF_Status* SWIG typemap and deprecate this.
550@tf_export(v1=["errors.raise_exception_on_not_ok_status"])  # pylint: disable=invalid-name
551class raise_exception_on_not_ok_status(object):
552  """Context manager to check for C API status."""
553
554  def __enter__(self):
555    self.status = c_api_util.ScopedTFStatus()
556    return self.status.status
557
558  def __exit__(self, type_arg, value_arg, traceback_arg):
559    try:
560      if c_api.TF_GetCode(self.status.status) != 0:
561        raise _make_specific_exception(
562            None, None, compat.as_text(c_api.TF_Message(self.status.status)),
563            c_api.TF_GetCode(self.status.status))
564    # Delete the underlying status object from memory otherwise it stays alive
565    # as there is a reference to status from this from the traceback due to
566    # raise.
567    finally:
568      del self.status
569    return False  # False values do not suppress exceptions
570