{"record":{"id":"e436a24f446d125a","repo":"hashicorp/nomad","slug":"csi-client-error-ignorable","errorCode":null,"errorMessage":"CSI client error (ignorable)","messagePattern":"CSI client error \\(ignorable\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"nomad/structs/errors.go","lineNumber":89,"sourceCode":"\t// evaluate a requested filter expression. api.ResultPaginatorErrorContent\n\t// duplicates its message so the CLI can match it without importing structs.\n\t// Keep the two in sync.\n\tErrResultPaginatorCreation = errors.New(errResultPaginatorCreation)\n\n\tErrUnknownNode = errors.New(ErrUnknownNodePrefix)\n\n\tErrDeploymentTerminalNoCancel    = errors.New(errDeploymentTerminalNoCancel)\n\tErrDeploymentTerminalNoFail      = errors.New(errDeploymentTerminalNoFail)\n\tErrDeploymentTerminalNoPause     = errors.New(errDeploymentTerminalNoPause)\n\tErrDeploymentTerminalNoPromote   = errors.New(errDeploymentTerminalNoPromote)\n\tErrDeploymentTerminalNoResume    = errors.New(errDeploymentTerminalNoResume)\n\tErrDeploymentTerminalNoUnblock   = errors.New(errDeploymentTerminalNoUnblock)\n\tErrDeploymentTerminalNoRun       = errors.New(errDeploymentTerminalNoRun)\n\tErrDeploymentTerminalNoSetHealth = errors.New(errDeploymentTerminalNoSetHealth)\n\tErrDeploymentRunningNoUnblock    = errors.New(errDeploymentRunningNoUnblock)\n\n\tErrCSIClientRPCIgnorable  = errors.New(\"CSI client error (ignorable)\")\n\tErrCSIClientRPCRetryable  = errors.New(\"CSI client error (retryable)\")\n\tErrCSIVolumeMaxClaims     = errors.New(\"volume max claims reached\")\n\tErrCSIVolumeUnschedulable = errors.New(\"volume is currently unschedulable\")\n\tErrCSIPluginInUse         = errors.New(\"plugin in use\")\n)\n\n// IsErrNoLeader returns whether the error is due to there being no leader.\nfunc IsErrNoLeader(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), errNoLeader)\n}\n\n// IsErrNoRegionPath returns whether the error is due to there being no path to\n// the given region.\nfunc IsErrNoRegionPath(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), errNoRegionPath)\n}\n\n// IsErrTokenNotFound returns whether the error is due to the passed token not\n// being resolvable.","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/errors.go#L71-L107","documentation":"ErrCSIClientRPCIgnorable marks a CSI client-side RPC error as safe to ignore — the operation can proceed or be retried without consequence. Nomad wraps or string-matches this sentinel because CSI plugin errors cross RPC boundaries and lose their Go type, so callers use strings.Contains to detect it. It signals transient/harmless plugin responses rather than real failures.","triggerScenarios":"A CSI node/client plugin returns an error that Nomad classifies as ignorable during allocation hooks or client RPCs; callers like csi_hook.go match the message substring to decide retryability.","commonSituations":"A CSI plugin returns a transient error during an alloc runner hook; multiple CSI plugin instances respond inconsistently; plugin versions emit errors that are actually benign (e.g. already-detached volumes).","solutions":["Confirm the wrapped plugin error is genuinely transient, then simply retry the operation","In code, detect it via strings.Contains(err.Error(), structs.ErrCSIClientRPCIgnorable.Error()) as the codebase does, since errors.Is does not survive RPC wrapping","Log at debug level and continue — the error is classified as ignorable by design","If it fires persistently, inspect the CSI plugin logs for the underlying cause"],"exampleFix":"// before\nif err != nil {\n    return fmt.Errorf(\"csi hook failed: %w\", err)\n}\n// after\nif err != nil {\n    if strings.Contains(err.Error(), structs.ErrCSIClientRPCIgnorable.Error()) {\n        return nil // benign, safe to continue\n    }\n    return fmt.Errorf(\"csi hook failed: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// Check plugin health before RPC\nplugins, _, _ := client.CSIPlugins().List(nil)\nfor _, p := range plugins.Plugins {\n    if p.Provider == csiProvider && p.ControllersHealthy == 0 {\n        return fmt.Errorf(\"no healthy controllers for %s\", csiProvider)\n    }\n}","typeGuard":"func isIgnorableCSIErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), structs.ErrCSIClientRPCIgnorable.Error())\n}","tryCatchPattern":"if err != nil {\n    if isIgnorableCSIErr(err) {\n        return nil // safe to continue\n    }\n    return fmt.Errorf(\"csi rpc failed: %w\", err)\n}","preventionTips":["String-match the sentinel message, not errors.Is — the type is lost over RPC","Log ignorable CSI errors at debug level to reduce noise","Monitor persistent occurrences; they may mask real plugin problems"],"tags":["csi","storage","nomad"],"backgroundTag":"csi-plugin-transient-error","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}