From b1bfdd52cbab14f6d13634dda08fd58fc48fd8e7 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 21 May 2026 20:04:55 +0200 Subject: [PATCH 1/6] Bump Go toolchain to go1.26.3 Bump Go to `go1.26.0` / `toolchain go1.26.3` across all four modules. This cleans up an inconsistency: `go.mod`, `tools/task/go.mod`, and `bundle/internal/tf/codegen/go.mod` were sitting at `go 1.25.8` while `tools/go.mod` had drifted back to `go 1.25.0`. All four are now in sync. Release notes: https://go.dev/doc/go1.26 Co-authored-by: Isaac --- bundle/internal/tf/codegen/go.mod | 4 ++-- go.mod | 4 ++-- tools/go.mod | 4 ++-- tools/task/go.mod | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bundle/internal/tf/codegen/go.mod b/bundle/internal/tf/codegen/go.mod index 6d3956c8ad6..776c067a604 100644 --- a/bundle/internal/tf/codegen/go.mod +++ b/bundle/internal/tf/codegen/go.mod @@ -1,8 +1,8 @@ module github.com/databricks/cli/bundle/internal/tf/codegen -go 1.25.8 +go 1.26.0 -toolchain go1.25.10 +toolchain go1.26.3 require ( github.com/hashicorp/go-version v1.9.0 diff --git a/go.mod b/go.mod index 0bb3109463e..50c45ad551b 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/databricks/cli -go 1.25.8 +go 1.26.0 -toolchain go1.25.10 +toolchain go1.26.3 require ( dario.cat/mergo v1.0.2 // BSD-3-Clause diff --git a/tools/go.mod b/tools/go.mod index 4b84239abcc..23a03db6721 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,8 +1,8 @@ module github.com/databricks/cli/tools -go 1.25.0 +go 1.26.0 -toolchain go1.25.10 +toolchain go1.26.3 require github.com/stretchr/testify v1.11.1 diff --git a/tools/task/go.mod b/tools/task/go.mod index 623da6fa617..cc3f0201735 100644 --- a/tools/task/go.mod +++ b/tools/task/go.mod @@ -1,8 +1,8 @@ module github.com/databricks/cli/tools/task -go 1.25.8 +go 1.26.0 -toolchain go1.25.10 +toolchain go1.26.3 require ( cel.dev/expr v0.25.1 // indirect From 8893371b4476d55110628d7e2c691675f4b16714 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 21 May 2026 20:25:33 +0200 Subject: [PATCH 2/6] Apply golangci-lint --fix for Go 1.26 modernize Bumping the `go` directive to 1.26.0 unlocks two modernize analyzers that were silent on 1.25: * stditerators - prefer reflect.Type.Fields()/Methods() and reflect.Value.Fields()/Methods() over the classic NumField()/Field(i) loop pattern. * newexpr - replace local `*T` helpers like `func intPtr(v int) *int { return &v }` (and their callers) with Go 1.26's `new(expr)`. This commit is the verbatim output of `golangci-lint run --fix ./...`, applied at the same time as the toolchain bump so CI doesn't fail the moment the bump lands. A few `field := field` style redeclarations the fixer leaves behind are harmless (the loop variable is fresh per-iteration since Go 1.22). Leaving those for a follow-up cleanup rather than expanding the diff here. Co-authored-by: Isaac --- bundle/config/resources/dashboard_test.go | 8 ++++---- bundle/config/resources_test.go | 12 +++++------ .../terraform/tfdyn/convert_job_test.go | 8 ++++---- bundle/direct/dresources/app_test.go | 4 ++-- bundle/direct/dresources/util_test.go | 8 ++++---- bundle/docsgen/nodes_test.go | 11 +++++----- bundle/internal/schema/parser.go | 6 +++--- cmd/pipelines/history_test.go | 4 +++- libs/apps/runlocal/spec_test.go | 20 ++++++++++--------- libs/calladapt/validate.go | 8 ++++---- libs/cmdio/pager_test.go | 6 +++--- libs/jsonschema/from_type.go | 8 ++++---- libs/structs/structaccess/convert_test.go | 20 +++++++++++-------- libs/structs/structaccess/typecheck.go | 8 ++++---- libs/structs/structwalk/walktype.go | 4 ++-- 15 files changed, 72 insertions(+), 63 deletions(-) diff --git a/bundle/config/resources/dashboard_test.go b/bundle/config/resources/dashboard_test.go index c010d949157..0011833896e 100644 --- a/bundle/config/resources/dashboard_test.go +++ b/bundle/config/resources/dashboard_test.go @@ -29,8 +29,8 @@ func TestDashboardConfigIsSupersetOfSDKDashboard(t *testing.T) { // Create a map of SDK fields by name and their JSON tags sdkFields := make(map[string]string) - for i := range sdkType.NumField() { - field := sdkType.Field(i) + for field := range sdkType.Fields() { + field := field jsonTag := field.Tag.Get("json") jsonName := getJSONTagName(jsonTag) if jsonName != "" { @@ -40,8 +40,8 @@ func TestDashboardConfigIsSupersetOfSDKDashboard(t *testing.T) { // Create a map of config fields by name and their JSON tags configFields := make(map[string]string) - for i := range configType.NumField() { - field := configType.Field(i) + for field := range configType.Fields() { + field := field jsonTag := field.Tag.Get("json") jsonName := getJSONTagName(jsonTag) if jsonName != "" { diff --git a/bundle/config/resources_test.go b/bundle/config/resources_test.go index 1be4a84a1f8..a3d47b5b4b7 100644 --- a/bundle/config/resources_test.go +++ b/bundle/config/resources_test.go @@ -53,8 +53,8 @@ import ( func TestCustomMarshallerIsImplemented(t *testing.T) { rt := reflect.TypeFor[Resources]() - for i := range rt.NumField() { - field := rt.Field(i) + for field := range rt.Fields() { + field := field // Fields in Resources are expected be of the form map[string]*resourceStruct assert.Equal(t, reflect.Map, field.Type.Kind(), "Resource %s is not a map", field.Name) @@ -95,8 +95,8 @@ func TestResourcesAllResourcesCompleteness(t *testing.T) { types = append(types, group.Description.PluralName) } - for i := range rt.NumField() { - field := rt.Field(i) + for field := range rt.Fields() { + field := field jsonTag := field.Tag.Get("json") if idx := strings.Index(jsonTag, ","); idx != -1 { @@ -112,8 +112,8 @@ func TestSupportedResources(t *testing.T) { actual := SupportedResources() typ := reflect.TypeFor[Resources]() - for i := range typ.NumField() { - field := typ.Field(i) + for field := range typ.Fields() { + field := field jsonTags := strings.Split(field.Tag.Get("json"), ",") pluralName := jsonTags[0] assert.Equal(t, actual[pluralName].PluralName, pluralName) diff --git a/bundle/deploy/terraform/tfdyn/convert_job_test.go b/bundle/deploy/terraform/tfdyn/convert_job_test.go index 1f67369e916..6624e3e2c6d 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_job_test.go @@ -296,8 +296,8 @@ func TestSupportedTypeTasksComplete(t *testing.T) { taskType := reflect.TypeFor[jobs.Task]() var tasksWithSource []string - for i := range taskType.NumField() { - field := taskType.Field(i) + for field := range taskType.Fields() { + field := field // Skip non-task fields (like DependsOn, Libraries, etc.) if !strings.HasSuffix(field.Name, "Task") { @@ -351,8 +351,8 @@ func findSourceFieldsShallow(t reflect.Type) []string { var paths []string - for i := range t.NumField() { - field := t.Field(i) + for field := range t.Fields() { + field := field // Check if this field is named "Source" if field.Name == "Source" { diff --git a/bundle/direct/dresources/app_test.go b/bundle/direct/dresources/app_test.go index edb99c4cffb..b1a20632550 100644 --- a/bundle/direct/dresources/app_test.go +++ b/bundle/direct/dresources/app_test.go @@ -149,8 +149,8 @@ func TestAppDoUpdate_UpdateMaskHasAllFields(t *testing.T) { fields := reflect.TypeFor[apps.App]() var allFields []string - for i := range fields.NumField() { - field := fields.Field(i) + for field := range fields.Fields() { + field := field jsonTag := field.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { continue diff --git a/bundle/direct/dresources/util_test.go b/bundle/direct/dresources/util_test.go index bf945aa09b5..e1630d0c5f9 100644 --- a/bundle/direct/dresources/util_test.go +++ b/bundle/direct/dresources/util_test.go @@ -12,15 +12,15 @@ import ( func assertFieldsCovered(t *testing.T, sdkType, remoteType reflect.Type, skip map[string]bool) { t.Helper() remoteFields := map[string]bool{} - for i := range remoteType.NumField() { - f := remoteType.Field(i) + for f := range remoteType.Fields() { + f := f if !f.Anonymous { remoteFields[f.Name] = true } } - for i := range sdkType.NumField() { - field := sdkType.Field(i) + for field := range sdkType.Fields() { + field := field if skip[field.Name] { assert.NotContains(t, remoteFields, field.Name, "field %s is in skip list but present in %s; remove it from skip", field.Name, remoteType.Name()) continue diff --git a/bundle/docsgen/nodes_test.go b/bundle/docsgen/nodes_test.go index 028bf1eff62..2b5bd6ca025 100644 --- a/bundle/docsgen/nodes_test.go +++ b/bundle/docsgen/nodes_test.go @@ -25,7 +25,7 @@ func TestBuildNodes_ChildExpansion(t *testing.T) { Items: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ - "listSub": {Reference: strPtr("#/$defs/github.com/listSub")}, + "listSub": {Reference: new("#/$defs/github.com/listSub")}, }, }, }, @@ -61,9 +61,9 @@ func TestBuildNodes_ChildExpansion(t *testing.T) { "myMap": { Type: "object", AdditionalProperties: &jsonschema.Schema{ - Reference: strPtr("#/$defs/github.com/myMap"), + Reference: new("#/$defs/github.com/myMap"), Properties: map[string]*jsonschema.Schema{ - "mapSub": {Type: "object", Reference: strPtr("#/$defs/github.com/mapSub")}, + "mapSub": {Type: "object", Reference: new("#/$defs/github.com/mapSub")}, }, }, }, @@ -73,7 +73,7 @@ func TestBuildNodes_ChildExpansion(t *testing.T) { "github.com/myMap": { Type: "object", Properties: map[string]*jsonschema.Schema{ - "mapSub": {Type: "boolean", Reference: strPtr("#/$defs/github.com/mapSub")}, + "mapSub": {Type: "boolean", Reference: new("#/$defs/github.com/mapSub")}, }, }, "github.com/mapSub": { @@ -171,6 +171,7 @@ func TestDoNotSuggestFields(t *testing.T) { assert.Equal(t, "nestedNotDoNotSuggestField", nodes[0].Attributes[0].Title) } +//go:fix inline func strPtr(s string) *string { - return &s + return new(s) } diff --git a/bundle/internal/schema/parser.go b/bundle/internal/schema/parser.go index d72524dc59c..ba0c8545ce2 100644 --- a/bundle/internal/schema/parser.go +++ b/bundle/internal/schema/parser.go @@ -58,13 +58,13 @@ func (p *openapiParser) findRef(typ reflect.Type) (jsonschema.Schema, bool) { // Check for embedded Databricks Go SDK types. if typ.Kind() == reflect.Struct { - for i := range typ.NumField() { - if !typ.Field(i).Anonymous { + for field := range typ.Fields() { + if !field.Anonymous { continue } // Deference current type if it's a pointer. - ctyp := typ.Field(i).Type + ctyp := field.Type for ctyp.Kind() == reflect.Ptr { ctyp = ctyp.Elem() } diff --git a/cmd/pipelines/history_test.go b/cmd/pipelines/history_test.go index 86d6d7d9d6e..59b9928b034 100644 --- a/cmd/pipelines/history_test.go +++ b/cmd/pipelines/history_test.go @@ -291,4 +291,6 @@ func TestFilterUpdates(t *testing.T) { } // Helper function to create int64 pointers -func int64Ptr(v int64) *int64 { return &v } +// +//go:fix inline +func int64Ptr(v int64) *int64 { return new(v) } diff --git a/libs/apps/runlocal/spec_test.go b/libs/apps/runlocal/spec_test.go index 3b3cfde51aa..7425e9ce778 100644 --- a/libs/apps/runlocal/spec_test.go +++ b/libs/apps/runlocal/spec_test.go @@ -28,11 +28,11 @@ func TestAppSpecLoadEnvVars(t *testing.T) { EnvVars: []AppEnvVar{ { Name: "VAR1", - Value: stringPtr("value1"), + Value: new("value1"), }, { Name: "VAR2", - Value: stringPtr("value2"), + Value: new("value2"), }, }, } @@ -53,11 +53,11 @@ func TestAppSpecLoadEnvVars(t *testing.T) { EnvVars: []AppEnvVar{ { Name: "VAR1", - ValueFrom: stringPtr("VAR1"), + ValueFrom: new("VAR1"), }, { Name: "VAR2", - ValueFrom: stringPtr("VAR2"), + ValueFrom: new("VAR2"), }, }, } @@ -77,11 +77,11 @@ func TestAppSpecLoadEnvVars(t *testing.T) { EnvVars: []AppEnvVar{ { Name: "VAR1", - Value: stringPtr("value1"), + Value: new("value1"), }, { Name: "VAR2", - ValueFrom: stringPtr("VAR2"), + ValueFrom: new("VAR2"), }, }, } @@ -101,7 +101,7 @@ func TestAppSpecLoadEnvVars(t *testing.T) { EnvVars: []AppEnvVar{ { Name: "VAR1", - ValueFrom: stringPtr("VAR1"), + ValueFrom: new("VAR1"), }, }, } @@ -121,7 +121,7 @@ func TestAppSpecLoadEnvVars(t *testing.T) { EnvVars: []AppEnvVar{ { Name: "VAR1", - ValueFrom: stringPtr("MISSING_VAR"), + ValueFrom: new("MISSING_VAR"), }, }, } @@ -151,6 +151,8 @@ func TestAppSpecLoadEnvVars(t *testing.T) { } // Helper function to create a string pointer +// +//go:fix inline func stringPtr(s string) *string { - return &s + return new(s) } diff --git a/libs/calladapt/validate.go b/libs/calladapt/validate.go index 8613d42d906..80b9023dc56 100644 --- a/libs/calladapt/validate.go +++ b/libs/calladapt/validate.go @@ -11,13 +11,13 @@ func EnsureNoExtraMethods(receiver any, ifaceTypes ...reflect.Type) error { allowed := make(map[string]struct{}) for _, ifaceType := range ifaceTypes { - for i := range ifaceType.NumMethod() { - allowed[ifaceType.Method(i).Name] = struct{}{} + for method := range ifaceType.Methods() { + allowed[method.Name] = struct{}{} } } - for i := range rt.NumMethod() { - m := rt.Method(i) + for m := range rt.Methods() { + m := m if _, ok := allowed[m.Name]; !ok { return fmt.Errorf("unexpected method %s on %v; only methods from %v are allowed", m.Name, rt, ifaceTypes) } diff --git a/libs/cmdio/pager_test.go b/libs/cmdio/pager_test.go index 645b26cbd04..3c76c1ee323 100644 --- a/libs/cmdio/pager_test.go +++ b/libs/cmdio/pager_test.go @@ -57,9 +57,9 @@ func printedText(t *testing.T, msg tea.Msg) string { t.Helper() rv := reflect.ValueOf(msg) require.Equal(t, reflect.Struct, rv.Kind(), "expected a struct msg, got %T", msg) - for i := range rv.NumField() { - if rv.Field(i).Kind() == reflect.String { - return rv.Field(i).String() + for _, field := range rv.Fields() { + if field.Kind() == reflect.String { + return field.String() } } t.Fatalf("no string field in %T", msg) diff --git a/libs/jsonschema/from_type.go b/libs/jsonschema/from_type.go index 356db14b6b8..8c6213b07ed 100644 --- a/libs/jsonschema/from_type.go +++ b/libs/jsonschema/from_type.go @@ -224,8 +224,8 @@ func getStructFields(typ reflect.Type) []reflect.StructField { var fields []reflect.StructField bfsQueue := list.New() - for i := range typ.NumField() { - bfsQueue.PushBack(typ.Field(i)) + for field := range typ.Fields() { + bfsQueue.PushBack(field) } for bfsQueue.Len() > 0 { front := bfsQueue.Front() @@ -246,8 +246,8 @@ func getStructFields(typ reflect.Type) []reflect.StructField { fieldType = fieldType.Elem() } - for i := range fieldType.NumField() { - bfsQueue.PushBack(fieldType.Field(i)) + for field := range fieldType.Fields() { + bfsQueue.PushBack(field) } } return fields diff --git a/libs/structs/structaccess/convert_test.go b/libs/structs/structaccess/convert_test.go index 02fcfc700ab..43f5003af12 100644 --- a/libs/structs/structaccess/convert_test.go +++ b/libs/structs/structaccess/convert_test.go @@ -61,22 +61,22 @@ func TestConvertToString(t *testing.T) { // Pointers { name: "string pointer", - value: stringPtr("test"), + value: new("test"), expected: "test", }, { name: "int pointer", - value: intPtr(123), + value: new(123), expected: "123", }, { name: "float64 pointer", - value: float64Ptr(2.5), + value: new(2.5), expected: "2.5", }, { name: "bool pointer", - value: boolPtr(true), + value: new(true), expected: "true", }, { @@ -147,18 +147,22 @@ func TestConvertToString(t *testing.T) { } } +//go:fix inline func stringPtr(s string) *string { - return &s + return new(s) } +//go:fix inline func intPtr(i int) *int { - return &i + return new(i) } +//go:fix inline func float64Ptr(f float64) *float64 { - return &f + return new(f) } +//go:fix inline func boolPtr(b bool) *bool { - return &b + return new(b) } diff --git a/libs/structs/structaccess/typecheck.go b/libs/structs/structaccess/typecheck.go index 2fa9a1f8d44..f2c91e13e5c 100644 --- a/libs/structs/structaccess/typecheck.go +++ b/libs/structs/structaccess/typecheck.go @@ -142,8 +142,8 @@ func FindStructFieldByKeyType(t reflect.Type, key string) (reflect.StructField, } // First pass: direct fields - for i := range t.NumField() { - sf := t.Field(i) + for sf := range t.Fields() { + sf := sf if sf.PkgPath != "" { // unexported continue } @@ -162,8 +162,8 @@ func FindStructFieldByKeyType(t reflect.Type, key string) (reflect.StructField, } // Second pass: search embedded anonymous structs recursively (flattening semantics) - for i := range t.NumField() { - sf := t.Field(i) + for sf := range t.Fields() { + sf := sf if !sf.Anonymous { continue } diff --git a/libs/structs/structwalk/walktype.go b/libs/structs/structwalk/walktype.go index 495a369d64a..dc7e958b600 100644 --- a/libs/structs/structwalk/walktype.go +++ b/libs/structs/structwalk/walktype.go @@ -104,8 +104,8 @@ func walkTypeValue(path *structpath.PatternNode, typ reflect.Type, field *reflec } func walkTypeStruct(path *structpath.PatternNode, st reflect.Type, visit VisitTypeFunc, visitedCount map[reflect.Type]int) { - for i := range st.NumField() { - sf := st.Field(i) + for sf := range st.Fields() { + sf := sf if sf.PkgPath != "" { continue // unexported } From 5313b9fb6a6006de3ada287f49185dfcfc64037d Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 21 May 2026 20:31:58 +0200 Subject: [PATCH 3/6] Clean up modernize fix-up output Follow-up to the previous commit, which the previous commit message deferred. Two cosmetic clean-ups: 1. Remove 14 redundant `x := x` redeclarations the modernize fixer left inside `for x := range t.Fields()` (or `.Methods()`) loops. The loop variable is already fresh per-iteration since Go 1.22, so the shadow was a no-op kept only to preserve the variable name from the old `field := t.Field(i)` pattern. 2. Remove dead `*Ptr` helper functions whose call sites were inlined to `new(expr)`. The fixer added `//go:fix inline` directives and rewrote the bodies to `return new(v)`, leaving the functions themselves with zero callers (except `int64Ptr`, where `new(700)` would yield `*int` instead of `*int64`; here we rewrite the 20 callers to `new(int64(N))` and drop the helper too). * bundle/docsgen/nodes_test.go: drop strPtr * libs/apps/runlocal/spec_test.go: drop stringPtr * libs/structs/structaccess/convert_test.go: drop stringPtr, intPtr, float64Ptr, boolPtr * cmd/pipelines/history_test.go: rewrite int64Ptr callers, drop helper Co-authored-by: Isaac --- bundle/config/resources/dashboard_test.go | 2 - bundle/config/resources_test.go | 4 -- .../terraform/tfdyn/convert_job_test.go | 4 -- bundle/direct/dresources/app_test.go | 1 - bundle/direct/dresources/util_test.go | 2 - bundle/docsgen/nodes_test.go | 5 --- cmd/pipelines/history_test.go | 45 +++++++++---------- libs/apps/runlocal/spec_test.go | 7 --- libs/calladapt/validate.go | 1 - libs/structs/structaccess/convert_test.go | 20 --------- libs/structs/structaccess/typecheck.go | 2 - libs/structs/structwalk/walktype.go | 1 - 12 files changed, 20 insertions(+), 74 deletions(-) diff --git a/bundle/config/resources/dashboard_test.go b/bundle/config/resources/dashboard_test.go index 0011833896e..83c80b17107 100644 --- a/bundle/config/resources/dashboard_test.go +++ b/bundle/config/resources/dashboard_test.go @@ -30,7 +30,6 @@ func TestDashboardConfigIsSupersetOfSDKDashboard(t *testing.T) { // Create a map of SDK fields by name and their JSON tags sdkFields := make(map[string]string) for field := range sdkType.Fields() { - field := field jsonTag := field.Tag.Get("json") jsonName := getJSONTagName(jsonTag) if jsonName != "" { @@ -41,7 +40,6 @@ func TestDashboardConfigIsSupersetOfSDKDashboard(t *testing.T) { // Create a map of config fields by name and their JSON tags configFields := make(map[string]string) for field := range configType.Fields() { - field := field jsonTag := field.Tag.Get("json") jsonName := getJSONTagName(jsonTag) if jsonName != "" { diff --git a/bundle/config/resources_test.go b/bundle/config/resources_test.go index a3d47b5b4b7..a75863d70b5 100644 --- a/bundle/config/resources_test.go +++ b/bundle/config/resources_test.go @@ -54,8 +54,6 @@ func TestCustomMarshallerIsImplemented(t *testing.T) { rt := reflect.TypeFor[Resources]() for field := range rt.Fields() { - field := field - // Fields in Resources are expected be of the form map[string]*resourceStruct assert.Equal(t, reflect.Map, field.Type.Kind(), "Resource %s is not a map", field.Name) kt := field.Type.Key() @@ -96,7 +94,6 @@ func TestResourcesAllResourcesCompleteness(t *testing.T) { } for field := range rt.Fields() { - field := field jsonTag := field.Tag.Get("json") if idx := strings.Index(jsonTag, ","); idx != -1 { @@ -113,7 +110,6 @@ func TestSupportedResources(t *testing.T) { typ := reflect.TypeFor[Resources]() for field := range typ.Fields() { - field := field jsonTags := strings.Split(field.Tag.Get("json"), ",") pluralName := jsonTags[0] assert.Equal(t, actual[pluralName].PluralName, pluralName) diff --git a/bundle/deploy/terraform/tfdyn/convert_job_test.go b/bundle/deploy/terraform/tfdyn/convert_job_test.go index 6624e3e2c6d..f7a69f77a01 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_job_test.go @@ -297,8 +297,6 @@ func TestSupportedTypeTasksComplete(t *testing.T) { var tasksWithSource []string for field := range taskType.Fields() { - field := field - // Skip non-task fields (like DependsOn, Libraries, etc.) if !strings.HasSuffix(field.Name, "Task") { continue @@ -352,8 +350,6 @@ func findSourceFieldsShallow(t reflect.Type) []string { var paths []string for field := range t.Fields() { - field := field - // Check if this field is named "Source" if field.Name == "Source" { paths = append(paths, "") diff --git a/bundle/direct/dresources/app_test.go b/bundle/direct/dresources/app_test.go index b1a20632550..c7cbfacd1af 100644 --- a/bundle/direct/dresources/app_test.go +++ b/bundle/direct/dresources/app_test.go @@ -150,7 +150,6 @@ func TestAppDoUpdate_UpdateMaskHasAllFields(t *testing.T) { fields := reflect.TypeFor[apps.App]() var allFields []string for field := range fields.Fields() { - field := field jsonTag := field.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { continue diff --git a/bundle/direct/dresources/util_test.go b/bundle/direct/dresources/util_test.go index e1630d0c5f9..544b3e2bde8 100644 --- a/bundle/direct/dresources/util_test.go +++ b/bundle/direct/dresources/util_test.go @@ -13,14 +13,12 @@ func assertFieldsCovered(t *testing.T, sdkType, remoteType reflect.Type, skip ma t.Helper() remoteFields := map[string]bool{} for f := range remoteType.Fields() { - f := f if !f.Anonymous { remoteFields[f.Name] = true } } for field := range sdkType.Fields() { - field := field if skip[field.Name] { assert.NotContains(t, remoteFields, field.Name, "field %s is in skip list but present in %s; remove it from skip", field.Name, remoteType.Name()) continue diff --git a/bundle/docsgen/nodes_test.go b/bundle/docsgen/nodes_test.go index 2b5bd6ca025..227d1bc01b3 100644 --- a/bundle/docsgen/nodes_test.go +++ b/bundle/docsgen/nodes_test.go @@ -170,8 +170,3 @@ func TestDoNotSuggestFields(t *testing.T) { assert.Len(t, nodes[0].Attributes, 1) assert.Equal(t, "nestedNotDoNotSuggestField", nodes[0].Attributes[0].Title) } - -//go:fix inline -func strPtr(s string) *string { - return new(s) -} diff --git a/cmd/pipelines/history_test.go b/cmd/pipelines/history_test.go index 59b9928b034..c1baec58327 100644 --- a/cmd/pipelines/history_test.go +++ b/cmd/pipelines/history_test.go @@ -24,25 +24,25 @@ func TestUpdatesBefore(t *testing.T) { }{ { name: "before 700", - timestamp: int64Ptr(700), + timestamp: new(int64(700)), expectedCount: 3, expectedFirst: 600, }, { name: "before 1000", - timestamp: int64Ptr(1000), + timestamp: new(int64(1000)), expectedCount: 5, expectedFirst: 1000, }, { name: "before 200", - timestamp: int64Ptr(200), + timestamp: new(int64(200)), expectedCount: 1, expectedFirst: 200, }, { name: "before 100", - timestamp: int64Ptr(100), + timestamp: new(int64(100)), expectedCount: 0, expectedFirst: 0, }, @@ -79,25 +79,25 @@ func TestUpdatesAfter(t *testing.T) { }{ { name: "after 500", - timestamp: int64Ptr(500), + timestamp: new(int64(500)), expectedCount: 3, expectedFirst: 1000, }, { name: "after 200", - timestamp: int64Ptr(200), + timestamp: new(int64(200)), expectedCount: 5, expectedFirst: 1000, }, { name: "after 1000", - timestamp: int64Ptr(1000), + timestamp: new(int64(1000)), expectedCount: 1, expectedFirst: 1000, }, { name: "after 1200", - timestamp: int64Ptr(1200), + timestamp: new(int64(1200)), expectedCount: 0, expectedFirst: 0, }, @@ -181,7 +181,7 @@ func TestFilterUpdates(t *testing.T) { name: "start time nil, end time set", updates: updates, startTime: nil, - endTime: int64Ptr(700), + endTime: new(int64(700)), expectedCount: 3, expectedFirst: 600, expectedLast: 200, @@ -189,7 +189,7 @@ func TestFilterUpdates(t *testing.T) { { name: "start time set, end time nil", updates: updates, - startTime: int64Ptr(500), + startTime: new(int64(500)), endTime: nil, expectedCount: 3, expectedFirst: 1000, @@ -198,8 +198,8 @@ func TestFilterUpdates(t *testing.T) { { name: "both times set within range", updates: updates, - startTime: int64Ptr(300), - endTime: int64Ptr(900), + startTime: new(int64(300)), + endTime: new(int64(900)), expectedCount: 3, expectedFirst: 800, expectedLast: 400, @@ -207,8 +207,8 @@ func TestFilterUpdates(t *testing.T) { { name: "both times set, no overlap", updates: updates, - startTime: int64Ptr(1200), - endTime: int64Ptr(1500), + startTime: new(int64(1200)), + endTime: new(int64(1500)), expectedCount: 0, expectedFirst: 0, expectedLast: 0, @@ -216,7 +216,7 @@ func TestFilterUpdates(t *testing.T) { { name: "start time after all updates", updates: updates, - startTime: int64Ptr(1200), + startTime: new(int64(1200)), endTime: nil, expectedCount: 0, expectedFirst: 0, @@ -226,7 +226,7 @@ func TestFilterUpdates(t *testing.T) { name: "end time before all updates", updates: updates, startTime: nil, - endTime: int64Ptr(100), + endTime: new(int64(100)), expectedCount: 0, expectedFirst: 0, expectedLast: 0, @@ -234,8 +234,8 @@ func TestFilterUpdates(t *testing.T) { { name: "start time after end time but within range", updates: updates, - startTime: int64Ptr(700), - endTime: int64Ptr(500), + startTime: new(int64(700)), + endTime: new(int64(500)), expectedCount: 0, expectedFirst: 0, expectedLast: 0, @@ -243,8 +243,8 @@ func TestFilterUpdates(t *testing.T) { { name: "start and end time match exact values in list", updates: updates, - startTime: int64Ptr(400), - endTime: int64Ptr(800), + startTime: new(int64(400)), + endTime: new(int64(800)), expectedCount: 3, expectedFirst: 800, expectedLast: 400, @@ -289,8 +289,3 @@ func TestFilterUpdates(t *testing.T) { }) } } - -// Helper function to create int64 pointers -// -//go:fix inline -func int64Ptr(v int64) *int64 { return new(v) } diff --git a/libs/apps/runlocal/spec_test.go b/libs/apps/runlocal/spec_test.go index 7425e9ce778..f4218c09e41 100644 --- a/libs/apps/runlocal/spec_test.go +++ b/libs/apps/runlocal/spec_test.go @@ -149,10 +149,3 @@ func TestAppSpecLoadEnvVars(t *testing.T) { }) } } - -// Helper function to create a string pointer -// -//go:fix inline -func stringPtr(s string) *string { - return new(s) -} diff --git a/libs/calladapt/validate.go b/libs/calladapt/validate.go index 80b9023dc56..e33697a79f4 100644 --- a/libs/calladapt/validate.go +++ b/libs/calladapt/validate.go @@ -17,7 +17,6 @@ func EnsureNoExtraMethods(receiver any, ifaceTypes ...reflect.Type) error { } for m := range rt.Methods() { - m := m if _, ok := allowed[m.Name]; !ok { return fmt.Errorf("unexpected method %s on %v; only methods from %v are allowed", m.Name, rt, ifaceTypes) } diff --git a/libs/structs/structaccess/convert_test.go b/libs/structs/structaccess/convert_test.go index 43f5003af12..f4ccbef0f05 100644 --- a/libs/structs/structaccess/convert_test.go +++ b/libs/structs/structaccess/convert_test.go @@ -146,23 +146,3 @@ func TestConvertToString(t *testing.T) { }) } } - -//go:fix inline -func stringPtr(s string) *string { - return new(s) -} - -//go:fix inline -func intPtr(i int) *int { - return new(i) -} - -//go:fix inline -func float64Ptr(f float64) *float64 { - return new(f) -} - -//go:fix inline -func boolPtr(b bool) *bool { - return new(b) -} diff --git a/libs/structs/structaccess/typecheck.go b/libs/structs/structaccess/typecheck.go index f2c91e13e5c..6e7c7d90ea4 100644 --- a/libs/structs/structaccess/typecheck.go +++ b/libs/structs/structaccess/typecheck.go @@ -143,7 +143,6 @@ func FindStructFieldByKeyType(t reflect.Type, key string) (reflect.StructField, // First pass: direct fields for sf := range t.Fields() { - sf := sf if sf.PkgPath != "" { // unexported continue } @@ -163,7 +162,6 @@ func FindStructFieldByKeyType(t reflect.Type, key string) (reflect.StructField, // Second pass: search embedded anonymous structs recursively (flattening semantics) for sf := range t.Fields() { - sf := sf if !sf.Anonymous { continue } diff --git a/libs/structs/structwalk/walktype.go b/libs/structs/structwalk/walktype.go index dc7e958b600..cd4be28c843 100644 --- a/libs/structs/structwalk/walktype.go +++ b/libs/structs/structwalk/walktype.go @@ -105,7 +105,6 @@ func walkTypeValue(path *structpath.PatternNode, typ reflect.Type, field *reflec func walkTypeStruct(path *structpath.PatternNode, st reflect.Type, visit VisitTypeFunc, visitedCount map[reflect.Type]int) { for sf := range st.Fields() { - sf := sf if sf.PkgPath != "" { continue // unexported } From 10c03956f1a738b2da3ac5adafba6176ef1d5d39 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 21 May 2026 20:44:36 +0200 Subject: [PATCH 4/6] Add changelog entry for Go 1.26.3 bump Co-authored-by: Isaac --- NEXT_CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index f2e569c609c..b7372336591 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -8,3 +8,6 @@ ### Bundles * The error reported when a direct-only resource (catalogs, external locations, vector search endpoints) is used with the terraform engine now also suggests setting `bundle.engine: direct` in `databricks.yml`, in addition to the `DATABRICKS_BUNDLE_ENGINE` environment variable ([#5295](https://github.com/databricks/cli/pull/5295)). + +### Dependency updates +* Bump Go toolchain to 1.26.3 ([#5302](https://github.com/databricks/cli/pull/5302)). From b52d87b161afe633d7ab664427d0a368fd2aff41 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 21 May 2026 20:12:05 +0200 Subject: [PATCH 5/6] Add lint rule banning errors.As in favor of errors.AsType Go 1.26 introduces errors.AsType[T error](err error) (T, bool): a generic, type-safe, faster alternative to errors.As(err, &target) that removes the need to declare a zero-valued target variable just to pass its address. This commit only adds the rule; lint will fail on the 70+ existing call sites. The follow-up commit migrates them. Co-authored-by: Isaac --- .golangci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.golangci.yaml b/.golangci.yaml index 6f41b1cee22..3769876e8f8 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -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.' analyze-types: true copyloopvar: check-alias: true From e5ad197ce26e862efff82f07c03f3210e9bbe167 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 21 May 2026 20:27:16 +0200 Subject: [PATCH 6/6] Migrate errors.As to errors.AsType[T] Go 1.26's errors.AsType[T] is a generic, type-safe replacement for the pattern of declaring a zero-valued target var and passing &target to errors.As. The generic form returns (T, bool) directly and is also faster because the type check happens at compile time. libs/errs/aggregate.go retains errors.As with a nolint directive: its As(target any) method forwards a dynamic type that cannot be expressed as a generic type parameter. Co-authored-by: Isaac --- bundle/config/mutator/translate_paths.go | 4 +- bundle/config/root.go | 3 +- bundle/deploy/resource_path_mkdir.go | 3 +- bundle/direct/bundle_plan.go | 4 +- bundle/direct/dresources/cluster.go | 3 +- bundle/direct/dresources/postgres_endpoint.go | 3 +- bundle/direct/dresources/util.go | 3 +- bundle/direct/util.go | 4 +- bundle/phases/destroy.go | 3 +- cmd/bundle/deployment/migrate.go | 3 +- cmd/bundle/generate/alert.go | 3 +- cmd/labs/localcache/jsonfile.go | 3 +- cmd/labs/project/interpreters.go | 3 +- cmd/root/auth.go | 4 +- cmd/root/flag_suggestions.go | 4 +- cmd/workspace/apps/errors.go | 7 ++-- cmd/workspace/workspace/export_dir.go | 4 +- cmd/workspace/workspace/overrides.go | 4 +- experimental/postgres/cmd/connect.go | 9 ++--- experimental/postgres/cmd/error.go | 4 +- integration/libs/filer/helpers_test.go | 3 +- libs/apps/logstream/streamer.go | 10 ++--- libs/auth/error.go | 7 ++-- libs/auth/storage/cache.go | 6 +-- libs/auth/storage/not_found_hint.go | 3 +- libs/clicompat/clicompat.go | 6 +-- libs/databrickscfg/cfgpickers/warehouses.go | 4 +- libs/databrickscfg/loader.go | 3 +- libs/diag/sdk_error.go | 8 ++-- libs/dyn/visit.go | 12 +++--- libs/errs/aggregate.go | 2 +- libs/filer/dbfs_client.go | 28 +++++++------- libs/filer/files_client.go | 37 +++++++++---------- libs/filer/workspace_files_client.go | 18 ++++----- libs/git/clone.go | 3 +- libs/psql/connect.go | 3 +- libs/telemetry/logger.go | 3 +- libs/template/config.go | 2 +- libs/template/helpers.go | 3 +- libs/template/renderer.go | 5 +-- libs/testproxy/server.go | 3 +- 41 files changed, 108 insertions(+), 139 deletions(-) diff --git a/bundle/config/mutator/translate_paths.go b/bundle/config/mutator/translate_paths.go index 99dd75dd787..dc730c1ff1c 100644 --- a/bundle/config/mutator/translate_paths.go +++ b/bundle/config/mutator/translate_paths.go @@ -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 diff --git a/bundle/config/root.go b/bundle/config/root.go index 6d4697cc1ba..caca8e1f1ad 100644 --- a/bundle/config/root.go +++ b/bundle/config/root.go @@ -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, diff --git a/bundle/deploy/resource_path_mkdir.go b/bundle/deploy/resource_path_mkdir.go index ab070b925e6..c697fd30dea 100644 --- a/bundle/deploy/resource_path_mkdir.go +++ b/bundle/deploy/resource_path_mkdir.go @@ -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) diff --git a/bundle/direct/bundle_plan.go b/bundle/direct/bundle_plan.go index eb80f49b687..f9142329abb 100644 --- a/bundle/direct/bundle_plan.go +++ b/bundle/direct/bundle_plan.go @@ -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, ¬Found) { + _, 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{ diff --git a/bundle/direct/dresources/cluster.go b/bundle/direct/dresources/cluster.go index 46148a2655f..34edce25168 100644 --- a/bundle/direct/dresources/cluster.go +++ b/bundle/direct/dresources/cluster.go @@ -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) diff --git a/bundle/direct/dresources/postgres_endpoint.go b/bundle/direct/dresources/postgres_endpoint.go index df9f79ebd3b..51829e6ef25 100644 --- a/bundle/direct/dresources/postgres_endpoint.go +++ b/bundle/direct/dresources/postgres_endpoint.go @@ -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) { diff --git a/bundle/direct/dresources/util.go b/bundle/direct/dresources/util.go index 658de7be528..39c7fcb284e 100644 --- a/bundle/direct/dresources/util.go +++ b/bundle/direct/dresources/util.go @@ -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 diff --git a/bundle/direct/util.go b/bundle/direct/util.go index 3b4c9abd651..f3a16692860 100644 --- a/bundle/direct/util.go +++ b/bundle/direct/util.go @@ -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 diff --git a/bundle/phases/destroy.go b/bundle/phases/destroy.go index 98e6f7fee2a..58f646d7b3b 100644 --- a/bundle/phases/destroy.go +++ b/bundle/phases/destroy.go @@ -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 } diff --git a/cmd/bundle/deployment/migrate.go b/cmd/bundle/deployment/migrate.go index 77d95e3533e..801e46f7918 100644 --- a/cmd/bundle/deployment/migrate.go +++ b/cmd/bundle/deployment/migrate.go @@ -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() diff --git a/cmd/bundle/generate/alert.go b/cmd/bundle/generate/alert.go index 87ba3eacb9e..f8c0cc2f409 100644 --- a/cmd/bundle/generate/alert.go +++ b/cmd/bundle/generate/alert.go @@ -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 diff --git a/cmd/labs/localcache/jsonfile.go b/cmd/labs/localcache/jsonfile.go index 1a7c6b940ce..d86ff2dd9e1 100644 --- a/cmd/labs/localcache/jsonfile.go +++ b/cmd/labs/localcache/jsonfile.go @@ -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 } diff --git a/cmd/labs/project/interpreters.go b/cmd/labs/project/interpreters.go index 5b6bff16fc2..dfa51a1a5ba 100644 --- a/cmd/labs/project/interpreters.go +++ b/cmd/labs/project/interpreters.go @@ -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 } diff --git a/cmd/root/auth.go b/cmd/root/auth.go index f458f0f4695..8b9bfd0810b 100644 --- a/cmd/root/auth.go +++ b/cmd/root/auth.go @@ -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 } diff --git a/cmd/root/flag_suggestions.go b/cmd/root/flag_suggestions.go index effef1fccac..8aa1d822774 100644 --- a/cmd/root/flag_suggestions.go +++ b/cmd/root/flag_suggestions.go @@ -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, ¬Exist) { + notExist, ok := errors.AsType[*pflag.NotExistError](err) + if !ok { return err } diff --git a/cmd/workspace/apps/errors.go b/cmd/workspace/apps/errors.go index 80ccc8af75f..cbcb2ce3860 100644 --- a/cmd/workspace/apps/errors.go +++ b/cmd/workspace/apps/errors.go @@ -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 } diff --git a/cmd/workspace/workspace/export_dir.go b/cmd/workspace/workspace/export_dir.go index b6b4b86c8ad..e7965bcda91 100644 --- a/cmd/workspace/workspace/export_dir.go +++ b/cmd/workspace/workspace/export_dir.go @@ -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 } diff --git a/cmd/workspace/workspace/overrides.go b/cmd/workspace/workspace/overrides.go index c57209b554b..74186ebaf70 100644 --- a/cmd/workspace/workspace/overrides.go +++ b/cmd/workspace/workspace/overrides.go @@ -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 == "" diff --git a/experimental/postgres/cmd/connect.go b/experimental/postgres/cmd/connect.go index 0cc47c998a0..79e7545b152 100644 --- a/experimental/postgres/cmd/connect.go +++ b/experimental/postgres/cmd/connect.go @@ -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": @@ -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" } diff --git a/experimental/postgres/cmd/error.go b/experimental/postgres/cmd/error.go index 02278a6c58b..5fdcf6eed99 100644 --- a/experimental/postgres/cmd/error.go +++ b/experimental/postgres/cmd/error.go @@ -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() } diff --git a/integration/libs/filer/helpers_test.go b/integration/libs/filer/helpers_test.go index a3a3aaae589..ead83d66b08 100644 --- a/integration/libs/filer/helpers_test.go +++ b/integration/libs/filer/helpers_test.go @@ -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) } diff --git a/libs/apps/logstream/streamer.go b/libs/apps/logstream/streamer.go index 5cbfc87ede9..16f4d4bf742 100644 --- a/libs/apps/logstream/streamer.go +++ b/libs/apps/logstream/streamer.go @@ -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 { @@ -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 @@ -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 { diff --git a/libs/auth/error.go b/libs/auth/error.go index 2ca8aa5f800..7cfb6b6fc76 100644 --- a/libs/auth/error.go +++ b/libs/auth/error.go @@ -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, @@ -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 { diff --git a/libs/auth/storage/cache.go b/libs/auth/storage/cache.go index c3d1fc43dcf..94a15a7ef0f 100644 --- a/libs/auth/storage/cache.go +++ b/libs/auth/storage/cache.go @@ -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 } @@ -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 } diff --git a/libs/auth/storage/not_found_hint.go b/libs/auth/storage/not_found_hint.go index d86615535b5..2e6e13e064d 100644 --- a/libs/auth/storage/not_found_hint.go +++ b/libs/auth/storage/not_found_hint.go @@ -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 "" diff --git a/libs/clicompat/clicompat.go b/libs/clicompat/clicompat.go index 8d982db132e..be9c27db4b5 100644 --- a/libs/clicompat/clicompat.go +++ b/libs/clicompat/clicompat.go @@ -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 @@ -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 } } diff --git a/libs/databrickscfg/cfgpickers/warehouses.go b/libs/databrickscfg/cfgpickers/warehouses.go index 9f0c617e34b..22c22d09022 100644 --- a/libs/databrickscfg/cfgpickers/warehouses.go +++ b/libs/databrickscfg/cfgpickers/warehouses.go @@ -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) } diff --git a/libs/databrickscfg/loader.go b/libs/databrickscfg/loader.go index 8f4f2a38dbc..5d942b0b7c0 100644 --- a/libs/databrickscfg/loader.go +++ b/libs/databrickscfg/loader.go @@ -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 diff --git a/libs/diag/sdk_error.go b/libs/diag/sdk_error.go index 1099dd738dc..d190498d6bf 100644 --- a/libs/diag/sdk_error.go +++ b/libs/diag/sdk_error.go @@ -10,8 +10,8 @@ import ( ) func FormatAPIErrorSummary(e error) string { - var apiErr *apierr.APIError - if !errors.As(e, &apiErr) { + apiErr, ok := errors.AsType[*apierr.APIError](e) + if !ok { return e.Error() } extra := strings.TrimSpace(fmt.Sprintf("%d %s", apiErr.StatusCode, apiErr.ErrorCode)) @@ -19,8 +19,8 @@ func FormatAPIErrorSummary(e error) string { } func FormatAPIErrorDetails(e error) string { - var apiErr *apierr.APIError - if !errors.As(e, &apiErr) { + apiErr, ok := errors.AsType[*apierr.APIError](e) + if !ok { return "" } diff --git a/libs/dyn/visit.go b/libs/dyn/visit.go index 1822c7db653..7ae00fa8e08 100644 --- a/libs/dyn/visit.go +++ b/libs/dyn/visit.go @@ -24,8 +24,8 @@ func (e cannotTraverseNilError) Error() string { } func IsCannotTraverseNilError(err error) bool { - var target cannotTraverseNilError - return errors.As(err, &target) + _, ok := errors.AsType[cannotTraverseNilError](err) + return ok } type noSuchKeyError struct { @@ -37,8 +37,8 @@ func (e noSuchKeyError) Error() string { } func IsNoSuchKeyError(err error) bool { - var target noSuchKeyError - return errors.As(err, &target) + _, ok := errors.AsType[noSuchKeyError](err) + return ok } type indexOutOfBoundsError struct { @@ -50,8 +50,8 @@ func (e indexOutOfBoundsError) Error() string { } func IsIndexOutOfBoundsError(err error) bool { - var target indexOutOfBoundsError - return errors.As(err, &target) + _, ok := errors.AsType[indexOutOfBoundsError](err) + return ok } type expectedMapToIndexError struct { diff --git a/libs/errs/aggregate.go b/libs/errs/aggregate.go index 08836494867..ba856100b30 100644 --- a/libs/errs/aggregate.go +++ b/libs/errs/aggregate.go @@ -60,7 +60,7 @@ func (ec errorChain) Unwrap() error { } func (ec errorChain) As(target any) bool { - return errors.As(ec[0], target) + return errors.As(ec[0], target) //nolint:forbidigo // forwarding the errors.As interface method; target type is dynamic and cannot use errors.AsType } func (ec errorChain) Is(target error) bool { diff --git a/libs/filer/dbfs_client.go b/libs/filer/dbfs_client.go index 761f279036d..ce9286ea8d9 100644 --- a/libs/filer/dbfs_client.go +++ b/libs/filer/dbfs_client.go @@ -98,8 +98,8 @@ func (w *DbfsClient) Write(ctx context.Context, name string, reader io.Reader, m if !slices.Contains(mode, CreateParentDirectories) { _, err = w.workspaceClient.Dbfs.GetStatusByPath(ctx, path.Dir(absPath)) if err != nil { - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -116,8 +116,8 @@ func (w *DbfsClient) Write(ctx context.Context, name string, reader io.Reader, m handle, err := w.workspaceClient.Dbfs.Open(ctx, absPath, fileMode) if err != nil { - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -153,8 +153,8 @@ func (w *DbfsClient) Read(ctx context.Context, name string) (io.ReadCloser, erro return nil, notAFile{absPath} } - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } @@ -190,8 +190,8 @@ func (w *DbfsClient) Delete(ctx context.Context, name string, mode ...DeleteMode // _, err = w.workspaceClient.Dbfs.GetStatusByPath(ctx, absPath) if err != nil { - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -218,8 +218,8 @@ func (w *DbfsClient) Delete(ctx context.Context, name string, mode ...DeleteMode } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -248,8 +248,8 @@ func (w *DbfsClient) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, e res, err := w.workspaceClient.Dbfs.ListByPath(ctx, absPath) if err != nil { - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } @@ -294,8 +294,8 @@ func (w *DbfsClient) Stat(ctx context.Context, name string) (fs.FileInfo, error) info, err := w.workspaceClient.Dbfs.GetStatusByPath(ctx, absPath) if err != nil { - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } diff --git a/libs/filer/files_client.go b/libs/filer/files_client.go index 2ac76166162..6abfa1e66c3 100644 --- a/libs/filer/files_client.go +++ b/libs/filer/files_client.go @@ -144,8 +144,8 @@ func (w *FilesClient) Write(ctx context.Context, name string, reader io.Reader, if !slices.Contains(mode, CreateParentDirectories) { err := w.workspaceClient.Files.GetDirectoryMetadataByDirectoryPath(ctx, path.Dir(absPath)) if err != nil { - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -172,8 +172,8 @@ func (w *FilesClient) Write(ctx context.Context, name string, reader io.Reader, } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -200,8 +200,8 @@ func (w *FilesClient) Read(ctx context.Context, name string) (io.ReadCloser, err } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } @@ -237,9 +237,9 @@ func (w *FilesClient) deleteFile(ctx context.Context, name string) error { return nil } - var aerr *apierr.APIError // Special handling of this error only if it is an API error. - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -264,9 +264,9 @@ func (w *FilesClient) deleteDirectory(ctx context.Context, name string) error { err = w.workspaceClient.Files.DeleteDirectoryByDirectoryPath(ctx, absPath) - var aerr *apierr.APIError // Special handling of this error only if it is an API error. - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -402,13 +402,13 @@ func (w *FilesClient) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, } // Special handling of this error only if it is an API error. - var apierr *apierr.APIError - if !errors.As(err, &apierr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } // This API returns a 404 if the specified path does not exist. - if apierr.StatusCode == http.StatusNotFound { + if aerr.StatusCode == http.StatusNotFound { // Check if the path is a file. If so, return not a directory error. if _, err := w.statFile(ctx, name); err == nil { return nil, notADirectory{absPath} @@ -431,8 +431,7 @@ func (w *FilesClient) Mkdir(ctx context.Context, name string) error { }) // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if errors.As(err, &aerr) && aerr.StatusCode == http.StatusConflict { + if aerr, ok := errors.AsType[*apierr.APIError](err); ok && aerr.StatusCode == http.StatusConflict { return fileAlreadyExistsError{absPath} } @@ -458,8 +457,8 @@ func (w *FilesClient) statFile(ctx context.Context, name string) (fs.FileInfo, e } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } @@ -486,8 +485,8 @@ func (w *FilesClient) statDir(ctx context.Context, name string) (fs.FileInfo, er } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } diff --git a/libs/filer/workspace_files_client.go b/libs/filer/workspace_files_client.go index a23c97724cf..3663473e92a 100644 --- a/libs/filer/workspace_files_client.go +++ b/libs/filer/workspace_files_client.go @@ -179,8 +179,8 @@ func (w *WorkspaceFilesClient) Write(ctx context.Context, name string, reader io } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -193,7 +193,7 @@ func (w *WorkspaceFilesClient) Write(ctx context.Context, name string, reader io // Create parent directory. err = w.workspaceClient.Workspace.MkdirsByPath(ctx, path.Dir(absPath)) //nolint:staticcheck // Deprecated in SDK v0.127.0. Migration to WorkspaceHierarchyService tracked separately. if err != nil { - if errors.As(err, &aerr) && aerr.StatusCode == http.StatusForbidden { + if mkdirErr, ok := errors.AsType[*apierr.APIError](err); ok && mkdirErr.StatusCode == http.StatusForbidden { return permissionError{absPath} } return fmt.Errorf("unable to mkdir to write file %s: %w", absPath, err) @@ -274,8 +274,8 @@ func (w *WorkspaceFilesClient) Delete(ctx context.Context, name string, mode ... } // Special handling of this error only if it is an API error. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return err } @@ -307,8 +307,8 @@ func (w *WorkspaceFilesClient) ReadDir(ctx context.Context, name string) ([]fs.D if err != nil { // If we got an API error we deal with it below. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } @@ -359,8 +359,8 @@ func (w *WorkspaceFilesClient) Stat(ctx context.Context, name string) (fs.FileIn ) if err != nil { // If we got an API error we deal with it below. - var aerr *apierr.APIError - if !errors.As(err, &aerr) { + aerr, ok := errors.AsType[*apierr.APIError](err) + if !ok { return nil, err } diff --git a/libs/git/clone.go b/libs/git/clone.go index 9369686c994..17703b672d8 100644 --- a/libs/git/clone.go +++ b/libs/git/clone.go @@ -41,8 +41,7 @@ func (opts cloneOptions) clone(ctx context.Context) error { if errors.Is(err, exec.ErrNotFound) { return fmt.Errorf("please install git CLI to clone a repository: %w", err) } - var processErr *process.ProcessError - if errors.As(err, &processErr) { + if processErr, ok := errors.AsType[*process.ProcessError](err); ok { return fmt.Errorf("git clone failed: %w. %s", err, processErr.Stderr) } if err != nil { diff --git a/libs/psql/connect.go b/libs/psql/connect.go index 2cb89e95956..91ecada0f21 100644 --- a/libs/psql/connect.go +++ b/libs/psql/connect.go @@ -147,8 +147,7 @@ func attemptConnection(ctx context.Context, args, env []string) error { err = cmd.Wait() if err != nil { - var exitError *exec.ExitError - if errors.As(err, &exitError) { + if exitError, ok := errors.AsType[*exec.ExitError](err); ok { // psql returns exit code 2 for fatal errors if exitError.ExitCode() == 2 { connErr := fmt.Errorf("connection failed: psql exited with code %d", exitError.ExitCode()) diff --git a/libs/telemetry/logger.go b/libs/telemetry/logger.go index d56e087322e..f9df3a97f7b 100644 --- a/libs/telemetry/logger.go +++ b/libs/telemetry/logger.go @@ -153,8 +153,7 @@ func Upload(ctx context.Context, ec protos.ExecutionContext) error { // // The UI infra team (who owns the /telemetry-ext API) recommends retrying for // all 5xx responses. - var apiErr *apierr.APIError - if errors.As(err, &apiErr) && apiErr.StatusCode >= 500 { + if apiErr, ok := errors.AsType[*apierr.APIError](err); ok && apiErr.StatusCode >= 500 { log.Infof(ctx, "Attempt %d failed due to a server side error. Retrying status code: %d", i+1, apiErr.StatusCode) remainingTime := time.Until(deadline) diff --git a/libs/template/config.go b/libs/template/config.go index 020d155cfa3..9183b6d09fe 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -300,7 +300,7 @@ func (c *config) promptForValues(r *renderer) error { if err == nil { break } - if !errors.As(err, &retriableError{}) { + if _, ok := errors.AsType[retriableError](err); !ok { return err } } diff --git a/libs/template/helpers.go b/libs/template/helpers.go index eefb79537ef..16c5ee27235 100644 --- a/libs/template/helpers.go +++ b/libs/template/helpers.go @@ -147,8 +147,7 @@ func loadHelpers(ctx context.Context) template.FuncMap { if cachedCatalog == nil { metastore, err := w.Metastores.Current(ctx) if err != nil { - var aerr *apierr.APIError - if errors.As(err, &aerr) && (slices.Contains(metastoreDisabledErrorCodes, aerr.ErrorCode) || aerr.Message == "Bad Target: /api/2.1/unity-catalog/current-metastore-assignment") { + if aerr, ok := errors.AsType[*apierr.APIError](err); ok && (slices.Contains(metastoreDisabledErrorCodes, aerr.ErrorCode) || aerr.Message == "Bad Target: /api/2.1/unity-catalog/current-metastore-assignment") { // Ignore: access denied or workspace doesn't have a metastore assigned empty_default := "" cachedCatalog = &empty_default diff --git a/libs/template/renderer.go b/libs/template/renderer.go index c1403fa071c..94745da2b0b 100644 --- a/libs/template/renderer.go +++ b/libs/template/renderer.go @@ -136,8 +136,7 @@ func (r *renderer) executeTemplate(templateDefinition string) (string, error) { if err != nil { // Parse and return a more readable error for missing values that are used // by the template definition but are not provided in the passed config. - target := &template.ExecError{} - if errors.As(err, target) { + if target, ok := errors.AsType[template.ExecError](err); ok { captureRegex := regexp.MustCompile(`map has no entry for key "(.*)"`) matches := captureRegex.FindStringSubmatch(target.Err.Error()) if len(matches) != 2 { @@ -200,7 +199,7 @@ func (r *renderer) computeFile(relPathTemplate string) (file, error) { } content, err := r.executeTemplate(string(contentTemplate)) // Capture errors caused by the "fail" helper function - if target := (&ErrFail{}); errors.As(err, target) { + if target, ok := errors.AsType[ErrFail](err); ok { return nil, target } if err != nil { diff --git a/libs/testproxy/server.go b/libs/testproxy/server.go index fd6038c8ed5..ccb67df1147 100644 --- a/libs/testproxy/server.go +++ b/libs/testproxy/server.go @@ -133,8 +133,7 @@ func (s *ProxyServer) proxyToCloud(w http.ResponseWriter, r *http.Request) { // exactly what the workspace returned. Re-marshalling from the parsed // APIError would drop fields the SDK doesn't surface (e.g. metadata in // details[]) and silently break callers that inspect them. - apiErr := &apierr.APIError{} - if errors.As(err, &apiErr) { + if apiErr, ok := errors.AsType[*apierr.APIError](err); ok { rw := apiErr.ResponseWrapper if rw == nil { // The SDK populates ResponseWrapper for every APIError produced