Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ linters:
msg: Use errors.Is(err, fs.ErrPermission) instead.
- pattern: 'sync\.Once\b($|[^FV])'
msg: Use sync.OnceFunc, sync.OnceValue, or sync.OnceValues instead.
- pattern: 'errors\.As\b'
msg: 'Use errors.AsType[T](err) for type-safe error unwrapping (Go 1.26+). errors.As remains available but the generic form removes the need for a target variable.'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
msg: 'Use errors.AsType[T](err) for type-safe error unwrapping (Go 1.26+). errors.As remains available but the generic form removes the need for a target variable.'
msg: 'Use errors.AsType[T](err) for type-safe error unwrapping (Go 1.26+).'

rule forbids errors.As so saying it remains available is confusing

analyze-types: true
copyloopvar:
check-alias: true
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/mutator/translate_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ func (t *translateContext) translateLocalRelativeWithPrefixPath(ctx context.Cont
func (t *translateContext) rewriteValue(ctx context.Context, p dyn.Path, v dyn.Value, dir string, opts translateOptions) (dyn.Value, error) {
out, err := t.rewritePath(ctx, dir, v.MustString(), opts)
if err != nil {
if target := (&ErrIsNotebook{}); errors.As(err, target) {
if target, ok := errors.AsType[ErrIsNotebook](err); ok {
return dyn.InvalidValue, fmt.Errorf(`expected a file for "%s" but got a notebook: %w`, p, target)
}
if target := (&ErrIsNotNotebook{}); errors.As(err, target) {
if target, ok := errors.AsType[ErrIsNotNotebook](err); ok {
return dyn.InvalidValue, fmt.Errorf(`expected a notebook for "%s" but got a file: %w`, p, target)
}
return dyn.InvalidValue, err
Expand Down
3 changes: 1 addition & 2 deletions bundle/config/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ func LoadFromBytes(path string, raw []byte) (*Root, diag.Diagnostics) {
// Load configuration tree from YAML.
v, err := yamlloader.LoadYAML(path, bytes.NewBuffer(raw))
if err != nil {
var le *yamlloader.LocationError
if errors.As(err, &le) {
if le, ok := errors.AsType[*yamlloader.LocationError](err); ok {
return nil, diag.Diagnostics{{
Severity: diag.Error,
Summary: le.Summary,
Expand Down
3 changes: 1 addition & 2 deletions bundle/deploy/resource_path_mkdir.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ func (m *resourcePathMkdir) Apply(ctx context.Context, b *bundle.Bundle) diag.Di

// Optimisitcally create the resource path. If it already exists ignore the error.
err := w.Workspace.MkdirsByPath(ctx, b.Config.Workspace.ResourcePath) //nolint:staticcheck // Deprecated in SDK v0.127.0. Migration to WorkspaceHierarchyService tracked separately.
var aerr *apierr.APIError
if errors.As(err, &aerr) && aerr.ErrorCode == "RESOURCE_ALREADY_EXISTS" {
if aerr, ok := errors.AsType[*apierr.APIError](err); ok && aerr.ErrorCode == "RESOURCE_ALREADY_EXISTS" {
return nil
}
return diag.FromErr(err)
Expand Down
4 changes: 2 additions & 2 deletions bundle/direct/bundle_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ func prepareChanges(ctx context.Context, adapter *dresources.Adapter, localDiff,
// we have difference for remoteState but not difference for localState
// from remoteDiff we can find out remote value (ch.Old) and new config value (ch.New) but we don't know oldState value
oldStateVal, err := structaccess.Get(oldState, ch.Path)
var notFound *structaccess.NotFoundError
if err != nil && !errors.As(err, &notFound) {
_, isNotFound := errors.AsType[*structaccess.NotFoundError](err)
if err != nil && !isNotFound {
log.Debugf(ctx, "Constructing diff: accessing %q on %T: %s", ch.Path, oldState, err)
}
m[ch.Path.String()] = &deployplan.ChangeDesc{
Expand Down
3 changes: 1 addition & 2 deletions bundle/direct/dresources/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ func (r *ResourceCluster) DoUpdate(ctx context.Context, id string, config *compu
return wait, nil
}

var apiErr *apierr.APIError
// Only Running and Terminated clusters can be modified. In particular, autoscaling clusters cannot be modified
// while the resizing is ongoing. We retry in this case. Scaling can take several minutes.
if errors.As(err, &apiErr) && apiErr.ErrorCode == "INVALID_STATE" {
if apiErr, ok := errors.AsType[*apierr.APIError](err); ok && apiErr.ErrorCode == "INVALID_STATE" {
return nil, retries.Continues(fmt.Sprintf("cluster %s cannot be modified in its current state: %s", id, apiErr.Message))
}
return nil, retries.Halt(err)
Expand Down
3 changes: 1 addition & 2 deletions bundle/direct/dresources/postgres_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,7 @@ func (r *ResourcePostgresEndpoint) DoDelete(ctx context.Context, id string) erro
})
if err != nil {
// Check if this is a reconciliation in progress error
var apiErr *apierr.APIError
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusConflict &&
if apiErr, ok := errors.AsType[*apierr.APIError](err); ok && apiErr.StatusCode == http.StatusConflict &&
strings.Contains(apiErr.Message, "reconciliation") {
// Check if we've exceeded the timeout
if time.Now().After(deadline) {
Expand Down
3 changes: 1 addition & 2 deletions bundle/direct/dresources/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import (
// This is copied from the retries package of the databricks-sdk-go. It should be made public,
// but for now, I'm copying it here.
func shouldRetry(err error) bool {
var e *retries.Err
if errors.As(err, &e) {
if e, ok := errors.AsType[*retries.Err](err); ok {
return !e.Halt
}
return false
Expand Down
4 changes: 2 additions & 2 deletions bundle/direct/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func isResourceGone(err error) bool {
// the parent's Delete will cascade-clean. Mirrors the TF provider's
// declarative.IsDeleteError suppression.
func isManagedByParent(err error) bool {
var apiErr *apierr.APIError
if !errors.As(err, &apiErr) || apiErr == nil {
apiErr, ok := errors.AsType[*apierr.APIError](err)
if !ok || apiErr == nil {
return false
}
info := apiErr.ErrorDetails().ErrorInfo
Expand Down
3 changes: 1 addition & 2 deletions bundle/phases/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ func assertRootPathExists(ctx context.Context, b *bundle.Bundle) (bool, error) {
w := b.WorkspaceClient(ctx)
_, err := w.Workspace.GetStatusByPath(ctx, b.Config.Workspace.RootPath) //nolint:staticcheck // Deprecated in SDK v0.127.0. Migration to WorkspaceHierarchyService tracked separately.

var aerr *apierr.APIError
if errors.As(err, &aerr) && aerr.StatusCode == http.StatusNotFound {
if aerr, ok := errors.AsType[*apierr.APIError](err); ok && aerr.StatusCode == http.StatusNotFound {
log.Infof(ctx, "Root path does not exist: %s", b.Config.Workspace.RootPath)
return false, nil
}
Expand Down
3 changes: 1 addition & 2 deletions cmd/bundle/deployment/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ func runPlanCheck(cmd *cobra.Command, extraArgs []string, extraArgsStr string) e
fmt.Fprint(cmd.OutOrStdout(), output)

if err != nil {
var exitErr *exec.ExitError
msg := ""
if errors.As(err, &exitErr) {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
msg = fmt.Sprintf("exit code %d", exitErr.ExitCode())
} else {
msg = err.Error()
Expand Down
3 changes: 1 addition & 2 deletions cmd/bundle/generate/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ After generation, you can deploy this alert to other targets using:
alert, err := w.AlertsV2.GetAlert(ctx, sql.GetAlertV2Request{Id: alertID})
if err != nil {
// Check if it's a not found error to provide a better message
var apiErr *apierr.APIError
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound {
if apiErr, ok := errors.AsType[*apierr.APIError](err); ok && apiErr.StatusCode == http.StatusNotFound {
return fmt.Errorf("alert with ID %s not found", alertID)
}
return err
Expand Down
3 changes: 1 addition & 2 deletions cmd/labs/localcache/jsonfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ type cached[T any] struct {

func (r *LocalCache[T]) refreshCache(ctx context.Context, refresh func() (T, error), offlineVal T) (T, error) {
data, err := refresh()
var urlError *url.Error
if errors.As(err, &urlError) {
if urlError, ok := errors.AsType[*url.Error](err); ok {
log.Warnf(ctx, "System offline. Cannot refresh cache: %s", urlError)
return offlineVal, nil
}
Expand Down
3 changes: 1 addition & 2 deletions cmd/labs/project/interpreters.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ func DetectInterpreters(ctx context.Context) (allInterpreters, error) {
// Keep in mind, that mswin installations get python.exe and pythonw.exe,
// which are slightly different: see https://stackoverflow.com/a/30313091
out, err := process.Background(ctx, []string{resolved, "--version"})
var processErr *process.ProcessError
if errors.As(err, &processErr) {
if processErr, ok := errors.AsType[*process.ProcessError](err); ok {
log.Debugf(ctx, "failed to check version for %s: %s", resolved, processErr.Err)
continue
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/root/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ func MustAnyClient(cmd *cobra.Command, args []string) (bool, error) {
// If the error indicates a wrong config type (workspace host used for account client,
// or config type mismatch detected by workspaceClientOrPrompt), fall through to try
// account client.
if !errors.Is(werr, errNotWorkspaceClient) && !errors.As(werr, &ErrNoWorkspaceProfiles{}) {
if _, ok := errors.AsType[ErrNoWorkspaceProfiles](werr); !errors.Is(werr, errNotWorkspaceClient) && !ok {
return false, werr
}

// Otherwise, the config used is account client one, so try to create an account client
aerr := MustAccountClient(cmd, args)
if errors.As(aerr, &ErrNoAccountProfiles{}) {
if _, ok := errors.AsType[ErrNoAccountProfiles](aerr); ok {
return false, aerr
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/root/flag_suggestions.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ func levenshteinDistance(a, b string) int {
// If a close match is found among the command's flags, it returns an enhanced error
// with a "Did you mean" suggestion appended. Otherwise it returns the original error.
func suggestFlagFromError(cmd *cobra.Command, err error) error {
var notExist *pflag.NotExistError
if !errors.As(err, &notExist) {
notExist, ok := errors.AsType[*pflag.NotExistError](err)
if !ok {
return err
}

Expand Down
7 changes: 3 additions & 4 deletions cmd/workspace/apps/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ const tailLinesSuggestedValue = 100
// These are errors wrapped by retries.Halt() during GetWithTimeout().
// Excludes API client errors (4xx) which are validation errors before deployment starts.
func isDeploymentWaitError(err error) bool {
var retriesErr *retries.Err
if !errors.As(err, &retriesErr) || !retriesErr.Halt {
retriesErr, ok := errors.AsType[*retries.Err](err)
if !ok || !retriesErr.Halt {
return false
}

// Exclude API client errors (4xx) (e.g. app not found)
var apiErr *apierr.APIError
if errors.As(err, &apiErr) && apiErr.StatusCode >= 400 && apiErr.StatusCode < 500 {
if apiErr, ok := errors.AsType[*apierr.APIError](err); ok && apiErr.StatusCode >= 400 && apiErr.StatusCode < 500 {
return false
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/workspace/workspace/export_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ type exportDirOptions struct {

// isFileSizeError checks if the error is due to file size limits.
func isFileSizeError(err error) bool {
var aerr *apierr.APIError
if !errors.As(err, &aerr) || aerr.StatusCode != http.StatusBadRequest {
aerr, ok := errors.AsType[*apierr.APIError](err)
if !ok || aerr.StatusCode != http.StatusBadRequest {
return false
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/workspace/workspace/overrides.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ func exportOverride(exportCmd *cobra.Command, exportReq *workspace.ExportRequest

// Give better errors / hints for common API errors.
func wrapImportAPIErrors(err error, importReq *workspace.Import) error {
apiErr := &apierr.APIError{}
if !errors.As(err, &apiErr) {
apiErr, ok := errors.AsType[*apierr.APIError](err)
if !ok {
return err
}
isFormatSource := importReq.Format == workspace.ImportFormatSource || importReq.Format == ""
Expand Down
9 changes: 3 additions & 6 deletions experimental/postgres/cmd/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ func isRetryableConnectError(err error) bool {
return false
}

var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok {
switch {
// 08xxx is the connection_exception class.
case len(pgErr.Code) == 5 && pgErr.Code[:2] == "08":
Expand All @@ -169,13 +168,11 @@ func isRetryableConnectError(err error) bool {
}
}

var connectErr *pgconn.ConnectError
if errors.As(err, &connectErr) {
if connectErr, ok := errors.AsType[*pgconn.ConnectError](err); ok {
return isRetryableConnectError(connectErr.Unwrap())
}

var opErr *net.OpError
if errors.As(err, &opErr) {
if opErr, ok := errors.AsType[*net.OpError](err); ok {
return opErr.Op == "dial"
}

Expand Down
4 changes: 2 additions & 2 deletions experimental/postgres/cmd/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
// surface it directly. The richer LINE+caret rendering is out of scope for
// this PR; we stick with the plain shape for now.
func formatPgError(err error) string {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
pgErr, ok := errors.AsType[*pgconn.PgError](err)
if !ok {
return err.Error()
}

Expand Down
3 changes: 1 addition & 2 deletions integration/libs/filer/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func setupWsfsFiler(t testutil.TestingT) (filer.Filer, string) {

// Check if we can use this API here, skip test if we cannot.
_, err = f.Read(ctx, "we_use_this_call_to_test_if_this_api_is_enabled")
var aerr *apierr.APIError
if errors.As(err, &aerr) && aerr.StatusCode == http.StatusBadRequest {
if aerr, ok := errors.AsType[*apierr.APIError](err); ok && aerr.StatusCode == http.StatusBadRequest {
t.Skip(aerr.Message)
}

Expand Down
10 changes: 4 additions & 6 deletions libs/apps/logstream/streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,7 @@ func (s *logStreamer) consume(ctx context.Context, conn *websocket.Conn) (retErr
if ctx.Err() != nil {
return ctx.Err()
}
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
if netErr, ok := errors.AsType[net.Error](err); ok && netErr.Timeout() {
if state.HasPendingFlushDeadline() {
shouldContinue, flushErr := state.HandleFlushTimeout()
if flushErr != nil {
Expand Down Expand Up @@ -308,8 +307,7 @@ func (s *logStreamer) shouldRefreshForStatus(respStatusCode *int) bool {
}

func (s *logStreamer) shouldRefreshForError(err error) bool {
var closeErr *websocket.CloseError
if errors.As(err, &closeErr) {
if closeErr, ok := errors.AsType[*websocket.CloseError](err); ok {
switch closeErr.Code {
case closeCodeUnauthorized, closeCodeForbidden:
return true
Expand All @@ -336,8 +334,8 @@ func decorateDialError(err error, resp *http.Response) error {
}

func handleCloseError(err error) (bool, error) {
var closeErr *websocket.CloseError
if !errors.As(err, &closeErr) {
closeErr, ok := errors.AsType[*websocket.CloseError](err)
if !ok {
return false, err
}
if closeErr.Code == websocket.CloseNormalClosure || closeErr.Code == websocket.CloseGoingAway {
Expand Down
7 changes: 3 additions & 4 deletions libs/auth/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ func AuthTypeDisplayName(authType string) string {
// RewriteAuthError rewrites the error message for invalid refresh token error.
// It returns whether the error was rewritten and the rewritten error.
func RewriteAuthError(ctx context.Context, host, accountId, profile string, err error) (bool, error) {
target := &u2m.InvalidRefreshTokenError{}
if errors.As(err, &target) {
if _, ok := errors.AsType[*u2m.InvalidRefreshTokenError](err); ok {
oauthArgument, err := AuthArguments{
Host: host,
AccountID: accountId,
Expand All @@ -73,8 +72,8 @@ func RewriteAuthError(ctx context.Context, host, accountId, profile string, err
// EnrichAuthError appends identity context and remediation steps to 401/403 API errors.
// For non-API errors or other status codes, the original error is returned unchanged.
func EnrichAuthError(ctx context.Context, cfg *config.Config, err error) error {
var apiErr *apierr.APIError
if !errors.As(err, &apiErr) {
apiErr, ok := errors.AsType[*apierr.APIError](err)
if !ok {
return err
}
if apiErr.StatusCode != http.StatusUnauthorized && apiErr.StatusCode != http.StatusForbidden {
Expand Down
6 changes: 2 additions & 4 deletions libs/auth/storage/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ func applyReadFallback(ctx context.Context, mode StorageMode, explicit bool, f c
return f.newKeyring(), mode, nil
}
if probeErr := f.probeKeyringRead(); probeErr != nil {
var timeoutErr *TimeoutError
if errors.As(probeErr, &timeoutErr) {
if _, ok := errors.AsType[*TimeoutError](probeErr); ok {
log.Debugf(ctx, "keyring read probe timed out (%v); staying on keyring", probeErr)
return f.newKeyring(), mode, nil
}
Expand Down Expand Up @@ -205,8 +204,7 @@ func applyLoginFallback(ctx context.Context, mode StorageMode, explicit bool, f
// during OAuth is the common case, and a misdiagnosed hang
// fails the final Store anyway, which is better than a
// silent plaintext downgrade.
var timeoutErr *TimeoutError
if errors.As(probeErr, &timeoutErr) {
if _, ok := errors.AsType[*TimeoutError](probeErr); ok {
log.Debugf(ctx, "keyring probe timed out (%v); staying on keyring", probeErr)
return f.newKeyring(), mode, nil
}
Expand Down
3 changes: 1 addition & 2 deletions libs/auth/storage/not_found_hint.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ func (e *notFoundHint) Unwrap() error { return cache.ErrNotFound }
// logic) but want to surface the actionable hint to the user instead of
// dropping it.
func HintForNotFound(err error) string {
var hint *notFoundHint
if errors.As(err, &hint) {
if hint, ok := errors.AsType[*notFoundHint](err); ok {
return hint.msg
}
return ""
Expand Down
6 changes: 2 additions & 4 deletions libs/clicompat/clicompat.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ func IsNotFoundError(err error) bool {
if errors.Is(err, ErrNotFound) {
return true
}
var httpErr *HTTPStatusError
if errors.As(err, &httpErr) && httpErr.StatusCode == http.StatusNotFound {
if httpErr, ok := errors.AsType[*HTTPStatusError](err); ok && httpErr.StatusCode == http.StatusNotFound {
return true
}
// Git clone errors include "not found" in stderr when a branch/tag does not
Expand Down Expand Up @@ -384,8 +383,7 @@ func fetchRemoteWithRetry(ctx context.Context) (Manifest, error) {
lastErr = err

// Do not retry client errors (4xx) — they won't resolve on retry.
var httpErr *HTTPStatusError
if errors.As(err, &httpErr) && httpErr.StatusCode >= 400 && httpErr.StatusCode < 500 {
if httpErr, ok := errors.AsType[*HTTPStatusError](err); ok && httpErr.StatusCode >= 400 && httpErr.StatusCode < 500 {
return nil, lastErr
}
}
Expand Down
4 changes: 2 additions & 2 deletions libs/databrickscfg/cfgpickers/warehouses.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ func GetDefaultWarehouse(ctx context.Context, w *databricks.WorkspaceClient) (*s
State: warehouse.State,
}, nil
}
var apiErr *apierr.APIError
if !errors.As(err, &apiErr) || apiErr.StatusCode >= 500 {
apiErr, ok := errors.AsType[*apierr.APIError](err)
if !ok || apiErr.StatusCode >= 500 {
return nil, fmt.Errorf("get default warehouse: %w", err)
}

Expand Down
3 changes: 1 addition & 2 deletions libs/databrickscfg/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ func (e errMultipleProfiles) Error() string {
// AsMultipleProfiles checks if the error is caused by multiple profiles
// matching the same host. If so, it returns the matching profile names.
func AsMultipleProfiles(err error) ([]string, bool) {
var e errMultipleProfiles
if errors.As(err, &e) {
if e, ok := errors.AsType[errMultipleProfiles](err); ok {
return []string(e), true
}
return nil, false
Expand Down
Loading
Loading