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