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