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