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 }