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