github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsmd/server_errors.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfsmd
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/kbfs/tlf"
    14  	"github.com/keybase/client/go/libkb"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    17  )
    18  
    19  const (
    20  	// StatusCodeServerError is the error code for a generic server error.
    21  	StatusCodeServerError = 2800
    22  	// StatusCodeServerErrorBadRequest is the error code for a generic client error.
    23  	StatusCodeServerErrorBadRequest = 2801
    24  	// StatusCodeServerErrorConflictRevision is the error code for a revision conflict error.
    25  	StatusCodeServerErrorConflictRevision = 2802
    26  	// StatusCodeServerErrorConflictPrevRoot is the error code for a PrevRoot pointer conflict error.
    27  	StatusCodeServerErrorConflictPrevRoot = 2803
    28  	// StatusCodeServerErrorConflictDiskUsage is the error code for a disk usage conflict error.
    29  	StatusCodeServerErrorConflictDiskUsage = 2804
    30  	// StatusCodeServerErrorLocked is the error code to indicate the folder truncation lock is locked.
    31  	StatusCodeServerErrorLocked = 2805
    32  	// StatusCodeServerErrorUnauthorized is the error code to indicate the client is unauthorized to perform
    33  	// a certain operation. This is also used to indicate an object isn't found.
    34  	StatusCodeServerErrorUnauthorized = 2806
    35  	// StatusCodeServerErrorThrottle is the error code to indicate the client should initiate backoff.
    36  	StatusCodeServerErrorThrottle = 2807
    37  	// StatusCodeServerErrorConditionFailed is the error code to indicate the write condition failed.
    38  	StatusCodeServerErrorConditionFailed = 2808
    39  	// StatusCodeServerErrorWriteAccess is the error code to indicate the client isn't authorized to
    40  	// write to a TLF.
    41  	StatusCodeServerErrorWriteAccess = 2809
    42  	// StatusCodeServerErrorConflictFolderMapping is the error code for a folder handle to folder ID
    43  	// mapping conflict error.
    44  	StatusCodeServerErrorConflictFolderMapping = 2810
    45  	// StatusCodeServerErrorTooManyFoldersCreated is the error code to
    46  	// indicate that the user has created more folders than their limit.
    47  	StatusCodeServerErrorTooManyFoldersCreated = 2811
    48  	// StatusCodeServerErrorCannotReadFinalizedTLF is the error code
    49  	// to indicate that a reader has requested to read a TLF ID that
    50  	// has been finalized, which isn't allowed.
    51  	StatusCodeServerErrorCannotReadFinalizedTLF = 2812
    52  	// StatusCodeServerErrorLockConflict is the error code returned by
    53  	// a MD write operation to indicate a lock conflict has happened and the MD
    54  	// has not been written. The lock conflict could be due to:
    55  	//   1) a lockID that client required the write to be contingent on is not
    56  	//      held at the time server tries to commit the MD, or
    57  	//   2) a implicit team migration lock is held on server, and the MD that
    58  	//      the client tried to write was either a rekey MD update or a MDv2
    59  	//      update, and was blocked by server.
    60  	StatusCodeServerErrorLockConflict = 2813
    61  	// StatusCodeServerErrorClassicTLFDoesNotExist is the error code returned by a
    62  	// MD get operation to indicate that a classic TLF is not found, and client
    63  	// has specified not to create one. Normally upon this error, KBFS client
    64  	// should ask service to create an implicit team for the give handle, and
    65  	// use the i-team backed TLF.
    66  	StatusCodeServerErrorClassicTLFDoesNotExist = 2814
    67  	// StatusCodeServerErrorMissingFolderHandle is the error code
    68  	// returned by the MD GetFolderHandle operation to indicate that a
    69  	// handle isn't found for a particular folder ID.  It's only
    70  	// returned if implicit teams is enabled, and likely indicates
    71  	// that the folder ID has been created by a client for an
    72  	// implicit-team-backed TLF, but no MDs have yet been written to
    73  	// the TLF.
    74  	StatusCodeServerErrorMissingFolderHandle = 2815
    75  )
    76  
    77  // ServerError is a generic server-side error.
    78  type ServerError struct {
    79  	Err error
    80  }
    81  
    82  // ToStatus implements the ExportableError interface for ServerError.
    83  func (e ServerError) ToStatus() (s keybase1.Status) {
    84  	s.Code = StatusCodeServerError
    85  	s.Name = "SERVER_ERROR"
    86  	s.Desc = e.Error()
    87  	return
    88  }
    89  
    90  // Error implements the Error interface for ServerError.
    91  func (e ServerError) Error() string {
    92  	if e.Err != nil {
    93  		return e.Err.Error()
    94  	}
    95  	return "ServerError"
    96  }
    97  
    98  // ServerErrorBadRequest is a generic client-side error.
    99  type ServerErrorBadRequest struct {
   100  	Reason string
   101  }
   102  
   103  // ToStatus implements the ExportableError interface for ServerErrorBadRequest.
   104  func (e ServerErrorBadRequest) ToStatus() (s keybase1.Status) {
   105  	s.Code = StatusCodeServerErrorBadRequest
   106  	s.Name = "BAD_REQUEST"
   107  	s.Desc = e.Reason
   108  	return
   109  }
   110  
   111  // Error implements the Error interface for ServerErrorBadRequest.
   112  func (e ServerErrorBadRequest) Error() string {
   113  	return fmt.Sprintf("Bad MD server request: %s", e.Reason)
   114  }
   115  
   116  // ServerErrorConflictRevision is returned when the passed MD block is inconsistent with current history.
   117  type ServerErrorConflictRevision struct {
   118  	Desc     string
   119  	Expected Revision
   120  	Actual   Revision
   121  }
   122  
   123  // Error implements the Error interface for ServerErrorConflictRevision.
   124  func (e ServerErrorConflictRevision) Error() string {
   125  	if e.Desc == "" {
   126  		return fmt.Sprintf("Conflict: expected revision %d, actual %d", e.Expected, e.Actual)
   127  	}
   128  	return "MDServerConflictRevision{" + e.Desc + "}"
   129  }
   130  
   131  // ToStatus implements the ExportableError interface for ServerErrorConflictRevision.
   132  func (e ServerErrorConflictRevision) ToStatus() (s keybase1.Status) {
   133  	s.Code = StatusCodeServerErrorConflictRevision
   134  	s.Name = "CONFLICT_REVISION"
   135  	s.Desc = e.Error()
   136  	return
   137  }
   138  
   139  // ServerErrorConflictPrevRoot is returned when the passed MD block is inconsistent with current history.
   140  type ServerErrorConflictPrevRoot struct {
   141  	Desc     string
   142  	Expected ID
   143  	Actual   ID
   144  }
   145  
   146  // Error implements the Error interface for ServerErrorConflictPrevRoot.
   147  func (e ServerErrorConflictPrevRoot) Error() string {
   148  	if e.Desc == "" {
   149  		return fmt.Sprintf("Conflict: expected previous root %v, actual %v", e.Expected, e.Actual)
   150  	}
   151  	return "MDServerConflictPrevRoot{" + e.Desc + "}"
   152  }
   153  
   154  // ToStatus implements the ExportableError interface for ServerErrorConflictPrevRoot.
   155  func (e ServerErrorConflictPrevRoot) ToStatus() (s keybase1.Status) {
   156  	s.Code = StatusCodeServerErrorConflictPrevRoot
   157  	s.Name = "CONFLICT_PREV_ROOT"
   158  	s.Desc = e.Error()
   159  	return
   160  }
   161  
   162  // ServerErrorConflictDiskUsage is returned when the passed MD block is inconsistent with current history.
   163  type ServerErrorConflictDiskUsage struct {
   164  	Desc     string
   165  	Expected uint64
   166  	Actual   uint64
   167  }
   168  
   169  // ToStatus implements the ExportableError interface for ServerErrorConflictDiskUsage.
   170  func (e ServerErrorConflictDiskUsage) ToStatus() (s keybase1.Status) {
   171  	s.Code = StatusCodeServerErrorConflictDiskUsage
   172  	s.Name = "CONFLICT_DISK_USAGE"
   173  	s.Desc = e.Error()
   174  	return
   175  }
   176  
   177  // Error implements the Error interface for ServerErrorConflictDiskUsage
   178  func (e ServerErrorConflictDiskUsage) Error() string {
   179  	if e.Desc == "" {
   180  		return fmt.Sprintf("Conflict: expected disk usage %d, actual %d", e.Expected, e.Actual)
   181  	}
   182  	return "ServerErrorConflictDiskUsage{" + e.Desc + "}"
   183  }
   184  
   185  // ServerErrorLocked is returned when the folder truncation lock is acquired by someone else.
   186  type ServerErrorLocked struct {
   187  }
   188  
   189  // Error implements the Error interface for ServerErrorLocked.
   190  func (e ServerErrorLocked) Error() string {
   191  	return "ServerErrorLocked{}"
   192  }
   193  
   194  // ToStatus implements the ExportableError interface for ServerErrorLocked.
   195  func (e ServerErrorLocked) ToStatus() (s keybase1.Status) {
   196  	s.Code = StatusCodeServerErrorLocked
   197  	s.Name = "LOCKED"
   198  	s.Desc = e.Error()
   199  	return
   200  }
   201  
   202  // ServerErrorUnauthorized is returned when a device requests a key half which doesn't belong to it.
   203  type ServerErrorUnauthorized struct {
   204  	Err error
   205  }
   206  
   207  // Error implements the Error interface for ServerErrorUnauthorized.
   208  func (e ServerErrorUnauthorized) Error() string {
   209  	msg := "MDServer Unauthorized"
   210  	if e.Err != nil {
   211  		msg += ": " + e.Err.Error()
   212  	}
   213  	return msg
   214  }
   215  
   216  // ToStatus implements the ExportableError interface for ServerErrorUnauthorized.
   217  func (e ServerErrorUnauthorized) ToStatus() (s keybase1.Status) {
   218  	s.Code = StatusCodeServerErrorUnauthorized
   219  	s.Name = "UNAUTHORIZED"
   220  	s.Desc = e.Error()
   221  	return
   222  }
   223  
   224  // ServerErrorWriteAccess is returned when the client isn't authorized to
   225  // write to a TLF.
   226  type ServerErrorWriteAccess struct{}
   227  
   228  // Error implements the Error interface for ServerErrorWriteAccess.
   229  func (e ServerErrorWriteAccess) Error() string {
   230  	return "ServerErrorWriteAccess{}"
   231  }
   232  
   233  // ToStatus implements the ExportableError interface for ServerErrorWriteAccess.
   234  func (e ServerErrorWriteAccess) ToStatus() (s keybase1.Status) {
   235  	s.Code = StatusCodeServerErrorWriteAccess
   236  	s.Name = "WRITE_ACCESS"
   237  	s.Desc = e.Error()
   238  	return
   239  }
   240  
   241  // ServerErrorThrottle is returned when the server wants the client to backoff.
   242  type ServerErrorThrottle struct {
   243  	Err              error
   244  	SuggestedRetryIn *time.Duration
   245  }
   246  
   247  // Error implements the Error interface for ServerErrorThrottle.
   248  func (e ServerErrorThrottle) Error() string {
   249  	if e.SuggestedRetryIn == nil {
   250  		return fmt.Sprintf("ServerErrorThrottle{%s}", e.Err.Error())
   251  	}
   252  	return fmt.Sprintf("ServerErrorThrottle[%s]{%s}", *e.SuggestedRetryIn, e.Err.Error())
   253  }
   254  
   255  // ToStatus implements the ExportableError interface for ServerErrorThrottle.
   256  func (e ServerErrorThrottle) ToStatus() (s keybase1.Status) {
   257  	s.Code = StatusCodeServerErrorThrottle
   258  	s.Name = "THROTTLE"
   259  	s.Desc = e.Err.Error()
   260  	if e.SuggestedRetryIn != nil {
   261  		s.Fields = append(s.Fields, keybase1.StringKVPair{
   262  			Key:   "suggestedRetryInMS",
   263  			Value: strconv.FormatInt(int64((*e.SuggestedRetryIn)/time.Millisecond), 10),
   264  		})
   265  	}
   266  	return
   267  }
   268  
   269  // ServerErrorConditionFailed is returned when a conditonal write failed.
   270  // This means there was a race and the caller should consider it a conflict.
   271  type ServerErrorConditionFailed struct {
   272  	Err            error
   273  	ShouldThrottle bool
   274  }
   275  
   276  // Error implements the Error interface for ServerErrorConditionFailed.
   277  func (e ServerErrorConditionFailed) Error() string {
   278  	return "ServerErrorConditionFailed{" + e.Err.Error() + "}"
   279  }
   280  
   281  // ToStatus implements the ExportableError interface for ServerErrorConditionFailed.
   282  func (e ServerErrorConditionFailed) ToStatus() (s keybase1.Status) {
   283  	s.Code = StatusCodeServerErrorConditionFailed
   284  	s.Name = "CONDITION_FAILED"
   285  	s.Desc = e.Err.Error()
   286  	s.Fields = []keybase1.StringKVPair{
   287  		{
   288  			Key:   "ShouldThrottle",
   289  			Value: strconv.FormatBool(e.ShouldThrottle),
   290  		},
   291  	}
   292  	return
   293  }
   294  
   295  // ServerErrorConflictFolderMapping is returned when there is a folder handle to folder
   296  // ID mapping mismatch.
   297  type ServerErrorConflictFolderMapping struct {
   298  	Desc     string
   299  	Expected tlf.ID
   300  	Actual   tlf.ID
   301  }
   302  
   303  // Error implements the Error interface for ServerErrorConflictFolderMapping.
   304  func (e ServerErrorConflictFolderMapping) Error() string {
   305  	if e.Desc == "" {
   306  		return fmt.Sprintf("Conflict: expected folder ID %s, actual %s",
   307  			e.Expected, e.Actual)
   308  	}
   309  	return "ServerErrorConflictFolderMapping{" + e.Desc + "}"
   310  }
   311  
   312  // ToStatus implements the ExportableError interface for ServerErrorConflictFolderMapping
   313  func (e ServerErrorConflictFolderMapping) ToStatus() (s keybase1.Status) {
   314  	s.Code = StatusCodeServerErrorConflictFolderMapping
   315  	s.Name = "CONFLICT_FOLDER_MAPPING"
   316  	s.Desc = e.Error()
   317  	return
   318  }
   319  
   320  // ServerErrorTooManyFoldersCreated is returned when a user has created more
   321  // folders than their limit allows.
   322  type ServerErrorTooManyFoldersCreated struct {
   323  	Created uint64
   324  	Limit   uint64
   325  }
   326  
   327  // Error implements the Error interface for ServerErrorTooManyFoldersCreated.
   328  func (e ServerErrorTooManyFoldersCreated) Error() string {
   329  	return fmt.Sprintf("Too many folders created. Created: %d, limit: %d",
   330  		e.Created, e.Limit)
   331  }
   332  
   333  // ToStatus implements the ExportableError interface for ServerErrorConflictFolderMapping
   334  func (e ServerErrorTooManyFoldersCreated) ToStatus() (s keybase1.Status) {
   335  	s.Code = StatusCodeServerErrorTooManyFoldersCreated
   336  	s.Name = "TOO_MANY_FOLDERS_CREATED"
   337  	s.Desc = e.Error()
   338  	s.Fields = []keybase1.StringKVPair{
   339  		{Key: "Limit", Value: strconv.FormatUint(e.Limit, 10)},
   340  		{Key: "Created", Value: strconv.FormatUint(e.Created, 10)},
   341  	}
   342  	return
   343  }
   344  
   345  // ServerErrorCannotReadFinalizedTLF is returned when the client
   346  // isn't authorized to read a finalized TLF.
   347  type ServerErrorCannotReadFinalizedTLF struct{}
   348  
   349  // Error implements the Error interface for
   350  // ServerErrorCannotReadFinalizedTLF.
   351  func (e ServerErrorCannotReadFinalizedTLF) Error() string {
   352  	return "ServerErrorCannotReadFinalizedTLF{}"
   353  }
   354  
   355  // ToStatus implements the ExportableError interface for
   356  // ServerErrorCannotReadFinalizedTLF.
   357  func (e ServerErrorCannotReadFinalizedTLF) ToStatus() (s keybase1.Status) {
   358  	s.Code = StatusCodeServerErrorCannotReadFinalizedTLF
   359  	s.Name = "CANNOT_READ_FINALIZED_TLF"
   360  	s.Desc = e.Error()
   361  	return
   362  }
   363  
   364  // ServerErrorLockConflict is the error type for
   365  // StatusCodeServerErrorLockConflict.
   366  type ServerErrorLockConflict struct{}
   367  
   368  // Error implements the Error interface.
   369  func (e ServerErrorLockConflict) Error() string {
   370  	return "This operation conflicted with another operation on the server, " +
   371  		"or it took too long. Please try again."
   372  }
   373  
   374  // ToStatus implements the ExportableError interface.
   375  func (e ServerErrorLockConflict) ToStatus() (s keybase1.Status) {
   376  	s.Code = StatusCodeServerErrorLockConflict
   377  	s.Name = "REQUIRED_LOCK_CONFLICT"
   378  	s.Desc = e.Error()
   379  	return
   380  }
   381  
   382  // ServerErrorClassicTLFDoesNotExist is the error type for
   383  // StatusCodeServerErrorClassicTLFDoesNotExist.
   384  type ServerErrorClassicTLFDoesNotExist struct{}
   385  
   386  // Error implements the Error interface.
   387  func (e ServerErrorClassicTLFDoesNotExist) Error() string {
   388  	return "ServerErrorClassicTLFDoesNotExist{}"
   389  }
   390  
   391  // ToStatus implements the ExportableError interface.
   392  func (e ServerErrorClassicTLFDoesNotExist) ToStatus() (s keybase1.Status) {
   393  	s.Code = StatusCodeServerErrorClassicTLFDoesNotExist
   394  	s.Name = "CLASSIC_TLF_DOES_NOT_EXIST"
   395  	s.Desc = e.Error()
   396  	return
   397  }
   398  
   399  // ServerErrorMissingFolderHandle is the error type for
   400  // StatusCodeServerErrorMissingFolderHandle.
   401  type ServerErrorMissingFolderHandle struct{}
   402  
   403  // Error implements the Error interface.
   404  func (e ServerErrorMissingFolderHandle) Error() string {
   405  	return "ServerErrorMissingFolderHandle{}"
   406  }
   407  
   408  // ToStatus implements the ExportableError interface.
   409  func (e ServerErrorMissingFolderHandle) ToStatus() (s keybase1.Status) {
   410  	s.Code = StatusCodeServerErrorMissingFolderHandle
   411  	s.Name = "MISSING_FOLDER_HANDLE"
   412  	s.Desc = e.Error()
   413  	return
   414  }
   415  
   416  // ServerErrorUnwrapper is an implementation of rpc.ErrorUnwrapper
   417  // for errors coming from the MDServer.
   418  type ServerErrorUnwrapper struct{}
   419  
   420  var _ rpc.ErrorUnwrapper = ServerErrorUnwrapper{}
   421  
   422  // MakeArg implements rpc.ErrorUnwrapper for ServerErrorUnwrapper.
   423  func (eu ServerErrorUnwrapper) MakeArg() interface{} {
   424  	return &keybase1.Status{}
   425  }
   426  
   427  // UnwrapError implements rpc.ErrorUnwrapper for ServerErrorUnwrapper.
   428  func (eu ServerErrorUnwrapper) UnwrapError(arg interface{}) (appError error, dispatchError error) {
   429  	s, ok := arg.(*keybase1.Status)
   430  	if !ok {
   431  		return nil, errors.New("Error converting arg to keybase1.Status object in ServerErrorUnwrapper.UnwrapError")
   432  	}
   433  
   434  	if s == nil || s.Code == 0 {
   435  		return nil, nil
   436  	}
   437  
   438  	switch s.Code {
   439  	case StatusCodeServerError:
   440  		appError = ServerError{errors.New(s.Desc)}
   441  	case StatusCodeServerErrorBadRequest:
   442  		appError = ServerErrorBadRequest{Reason: s.Desc}
   443  	case StatusCodeServerErrorConflictRevision:
   444  		appError = ServerErrorConflictRevision{Desc: s.Desc}
   445  	case StatusCodeServerErrorConflictPrevRoot:
   446  		appError = ServerErrorConflictPrevRoot{Desc: s.Desc}
   447  	case StatusCodeServerErrorConflictDiskUsage:
   448  		appError = ServerErrorConflictDiskUsage{Desc: s.Desc}
   449  	case StatusCodeServerErrorLocked:
   450  		appError = ServerErrorLocked{}
   451  	case StatusCodeServerErrorUnauthorized:
   452  		appError = ServerErrorUnauthorized{}
   453  	case StatusCodeServerErrorThrottle:
   454  		var suggestedRetryIn *time.Duration
   455  		for _, kv := range s.Fields {
   456  			if kv.Key == "suggestedRetryInMS" {
   457  				if ms, err := strconv.Atoi(kv.Value); err != nil {
   458  					d := time.Duration(ms) * time.Millisecond
   459  					suggestedRetryIn = &d
   460  				}
   461  				break
   462  			}
   463  		}
   464  		appError = ServerErrorThrottle{
   465  			Err:              errors.New(s.Desc),
   466  			SuggestedRetryIn: suggestedRetryIn,
   467  		}
   468  	case StatusCodeServerErrorConditionFailed:
   469  		shouldThrottle := false
   470  		for _, kv := range s.Fields {
   471  			if kv.Key == "ShouldThrottle" {
   472  				shouldThrottle, _ = strconv.ParseBool(kv.Value)
   473  				break
   474  			}
   475  		}
   476  		appError = ServerErrorConditionFailed{
   477  			Err:            errors.New(s.Desc),
   478  			ShouldThrottle: shouldThrottle,
   479  		}
   480  	case StatusCodeServerErrorWriteAccess:
   481  		appError = ServerErrorWriteAccess{}
   482  	case StatusCodeServerErrorConflictFolderMapping:
   483  		appError = ServerErrorConflictFolderMapping{Desc: s.Desc}
   484  	case StatusCodeServerErrorTooManyFoldersCreated:
   485  		err := ServerErrorTooManyFoldersCreated{}
   486  		for _, f := range s.Fields {
   487  			switch f.Key {
   488  			case "Limit":
   489  				err.Limit, _ = strconv.ParseUint(f.Value, 10, 64)
   490  			case "Created":
   491  				err.Created, _ = strconv.ParseUint(f.Value, 10, 64)
   492  			}
   493  		}
   494  		appError = err
   495  	case StatusCodeServerErrorCannotReadFinalizedTLF:
   496  		appError = ServerErrorCannotReadFinalizedTLF{}
   497  	case StatusCodeServerErrorLockConflict:
   498  		appError = ServerErrorLockConflict{}
   499  	case StatusCodeServerErrorClassicTLFDoesNotExist:
   500  		appError = ServerErrorClassicTLFDoesNotExist{}
   501  	case StatusCodeServerErrorMissingFolderHandle:
   502  		appError = ServerErrorMissingFolderHandle{}
   503  	default:
   504  		ase := libkb.AppStatusError{
   505  			Code:   s.Code,
   506  			Name:   s.Name,
   507  			Desc:   s.Desc,
   508  			Fields: make(map[string]string),
   509  		}
   510  		for _, f := range s.Fields {
   511  			ase.Fields[f.Key] = f.Value
   512  		}
   513  		appError = ase
   514  	}
   515  
   516  	return appError, nil
   517  }