diff --git a/.golangci.yaml b/.golangci.yaml index aa297e79221..f05060c41ae 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -38,10 +38,10 @@ linters: - errorlint settings: errorlint: - # Switching %v to %w changes which errors callers can match with - # errors.Is/As (e.g. fs.ErrNotExist, context.Canceled), so it needs - # per-site review rather than a bulk fix. - errorf: false + # errorf is on: switching %v to %w changes which errors callers can + # match with errors.Is/As, so keep %v with a nolint reason where the + # cause must not be visible to callers. + errorf: true modernize: # omitzero changes JSON wire format for nested structs (unlike omitempty), # so each site needs per-site review rather than a bulk rewrite. diff --git a/bundle/artifacts/build.go b/bundle/artifacts/build.go index e2bff61ad16..6c7ab622737 100644 --- a/bundle/artifacts/build.go +++ b/bundle/artifacts/build.go @@ -118,7 +118,7 @@ func doBuild(ctx context.Context, artifactName string, a *config.Artifact) error out, err := executor.Exec(ctx, a.BuildCommand) if err != nil { - return fmt.Errorf("build failed %s, error: %v, output: %s", artifactName, err, out) + return fmt.Errorf("build failed %s, error: %w, output: %s", artifactName, err, out) } log.Debugf(ctx, "build output for %s:\n%s", artifactName, out) diff --git a/bundle/config/mutator/python/python_diagnostics.go b/bundle/config/mutator/python/python_diagnostics.go index e47f6fb89ae..3006ee8321b 100644 --- a/bundle/config/mutator/python/python_diagnostics.go +++ b/bundle/config/mutator/python/python_diagnostics.go @@ -44,17 +44,17 @@ func parsePythonDiagnostics(input io.Reader) (diag.Diagnostics, error) { err := decoder.Decode(&parsedLine) if err != nil { - return nil, fmt.Errorf("failed to parse diags: %s", err) + return nil, fmt.Errorf("failed to parse diags: %w", err) } severity, err := convertPythonSeverity(parsedLine.Severity) if err != nil { - return nil, fmt.Errorf("failed to parse severity: %s", err) + return nil, fmt.Errorf("failed to parse severity: %w", err) } path, err := convertPythonPath(parsedLine.Path) if err != nil { - return nil, fmt.Errorf("failed to parse path: %s", err) + return nil, fmt.Errorf("failed to parse path: %w", err) } var paths []*structpath.PathNode if path != nil { diff --git a/bundle/config/mutator/python/python_locations.go b/bundle/config/mutator/python/python_locations.go index 5f85edfc488..bb301da9755 100644 --- a/bundle/config/mutator/python/python_locations.go +++ b/bundle/config/mutator/python/python_locations.go @@ -110,12 +110,12 @@ func parsePythonLocations(bundleRoot string, input io.Reader) (*pythonLocations, err := decoder.Decode(&entry) if err != nil { - return nil, fmt.Errorf("failed to parse python location: %s", err) + return nil, fmt.Errorf("failed to parse python location: %w", err) } path, err := dyn.NewPathFromString(entry.Path) if err != nil { - return nil, fmt.Errorf("failed to parse python location: %s", err) + return nil, fmt.Errorf("failed to parse python location: %w", err) } // Output can contain both relative paths and absolute paths outside of bundle root. diff --git a/bundle/config/mutator/resourcemutator/process_static_resources.go b/bundle/config/mutator/resourcemutator/process_static_resources.go index b1f689808f6..82aa89ff915 100644 --- a/bundle/config/mutator/resourcemutator/process_static_resources.go +++ b/bundle/config/mutator/resourcemutator/process_static_resources.go @@ -74,7 +74,7 @@ func getAllResources(b *bundle.Bundle) (ResourceKeySet, error) { return root, nil }) if err != nil { - return nil, fmt.Errorf("failed to collect resources: %s", err) + return nil, fmt.Errorf("failed to collect resources: %w", err) } return set, nil diff --git a/bundle/config/mutator/resourcemutator/resource_mutator.go b/bundle/config/mutator/resourcemutator/resource_mutator.go index 4d0b3bd47ab..6f6b3595e28 100644 --- a/bundle/config/mutator/resourcemutator/resource_mutator.go +++ b/bundle/config/mutator/resourcemutator/resource_mutator.go @@ -232,7 +232,7 @@ func NormalizeAndInitializeResources( return selectResources(root, addedResources) }) if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to select resources: %s", err)) + logdiag.LogError(ctx, fmt.Errorf("failed to select resources: %w", err)) return } @@ -251,7 +251,7 @@ func NormalizeAndInitializeResources( return mergeResources(root, snapshot) }) if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to merge resources: %s", err)) + logdiag.LogError(ctx, fmt.Errorf("failed to merge resources: %w", err)) } } @@ -274,7 +274,7 @@ func NormalizeResources( return selectResources(root, updatedResources) }) if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to select resources: %s", err)) + logdiag.LogError(ctx, fmt.Errorf("failed to select resources: %w", err)) return } @@ -299,7 +299,7 @@ func NormalizeResources( return mergeResources(root, snapshot) }) if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to merge resources: %s", err)) + logdiag.LogError(ctx, fmt.Errorf("failed to merge resources: %w", err)) } } diff --git a/bundle/config/mutator/set_variables.go b/bundle/config/mutator/set_variables.go index 3038b05acaf..f8f3b35394c 100644 --- a/bundle/config/mutator/set_variables.go +++ b/bundle/config/mutator/set_variables.go @@ -45,7 +45,7 @@ func setVariable(ctx context.Context, v dyn.Value, variable *variable.Variable, v, err := dyn.Set(v, "value", dyn.V(val)) if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to assign value "%s" to variable %s from environment variable %s with error: %v`, val, name, envVarName, err) + return dyn.InvalidValue, fmt.Errorf(`failed to assign value "%s" to variable %s from environment variable %s with error: %v`, val, name, envVarName, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors } return v, nil } @@ -70,7 +70,7 @@ func setVariable(ctx context.Context, v dyn.Value, variable *variable.Variable, v, err := dyn.Set(v, "value", fileDefault) if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to assign default value from variable file to variable %s with error: %v`, name, err) + return dyn.InvalidValue, fmt.Errorf(`failed to assign default value from variable file to variable %s with error: %v`, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors } return v, nil @@ -80,12 +80,12 @@ func setVariable(ctx context.Context, v dyn.Value, variable *variable.Variable, if variable.HasDefault() { vDefault, err := dyn.Get(v, "default") if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to get default value from config "%s" for variable %s with error: %v`, variable.Default, name, err) + return dyn.InvalidValue, fmt.Errorf(`failed to get default value from config "%s" for variable %s with error: %v`, variable.Default, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors } v, err := dyn.Set(v, "value", vDefault) if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to assign default value from config "%s" to variable %s with error: %v`, variable.Default, name, err) + return dyn.InvalidValue, fmt.Errorf(`failed to assign default value from config "%s" to variable %s with error: %v`, variable.Default, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors } return v, nil } diff --git a/bundle/config/root.go b/bundle/config/root.go index 3aa6fdd8b87..a64b742630e 100644 --- a/bundle/config/root.go +++ b/bundle/config/root.go @@ -289,7 +289,7 @@ func (r *Root) InitializeVariables(vars []string) error { err := r.Variables[name].Set(val) if err != nil { - return fmt.Errorf("failed to assign %s to %s: %s", val, name, err) + return fmt.Errorf("failed to assign %s to %s: %w", val, name, err) } } return nil diff --git a/bundle/deploy/files/delete.go b/bundle/deploy/files/delete.go index c30262f79ce..48eaf149d1c 100644 --- a/bundle/deploy/files/delete.go +++ b/bundle/deploy/files/delete.go @@ -66,7 +66,7 @@ func deleteSnapshotFile(ctx context.Context, b *bundle.Bundle) error { } err = os.Remove(sp) if err != nil && !errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("failed to destroy sync snapshot file: %s", err) + return fmt.Errorf("failed to destroy sync snapshot file: %w", err) } return nil } diff --git a/bundle/statemgmt/state_load.go b/bundle/statemgmt/state_load.go index abe260d4d4a..3c6b7df07b4 100644 --- a/bundle/statemgmt/state_load.go +++ b/bundle/statemgmt/state_load.go @@ -99,7 +99,7 @@ func ensureMap(v dyn.Value, path dyn.Path) (dyn.Value, error) { var err error v, err = dyn.SetByPath(v, path, dyn.V(dyn.NewMapping())) if err != nil { - return dyn.InvalidValue, fmt.Errorf("internal error: failed to create %s: %s", path, err) + return dyn.InvalidValue, fmt.Errorf("internal error: failed to create %s: %w", path, err) } } return v, nil diff --git a/experimental/ssh/internal/proxy/server.go b/experimental/ssh/internal/proxy/server.go index 1d0d9232f75..fd5a93417ba 100644 --- a/experimental/ssh/internal/proxy/server.go +++ b/experimental/ssh/internal/proxy/server.go @@ -145,7 +145,7 @@ func (server *proxyServer) handleNewConnection(ctx context.Context, w http.Respo func runServerProxy(ctx context.Context, proxy *proxyConnection, createServerCommand createServerCommandFunc, w http.ResponseWriter, r *http.Request) error { err := proxy.accept(w, r) if err != nil { - return fmt.Errorf("failed to upgrade to websockets: %v", err) + return fmt.Errorf("failed to upgrade to websockets: %w", err) } defer closeProxyConnection(ctx, proxy) log.Infof(ctx, "New connection accepted") @@ -159,17 +159,17 @@ func runServerProxy(ctx context.Context, proxy *proxyConnection, createServerCom sshdStdin, err := serverCmd.StdinPipe() if err != nil { - return fmt.Errorf("failed to get stdin pipe: %v", err) + return fmt.Errorf("failed to get stdin pipe: %w", err) } sshdStdout, err := serverCmd.StdoutPipe() if err != nil { - return fmt.Errorf("failed to get stdout pipe: %v", err) + return fmt.Errorf("failed to get stdout pipe: %w", err) } err = serverCmd.Start() if err != nil { - return fmt.Errorf("failed to start SSHD process: %v", err) + return fmt.Errorf("failed to start SSHD process: %w", err) } defer releaseServerCommand(ctx, serverCmd) diff --git a/libs/aitools/installer/installer.go b/libs/aitools/installer/installer.go index e3f39e09435..d7a0a165584 100644 --- a/libs/aitools/installer/installer.go +++ b/libs/aitools/installer/installer.go @@ -128,7 +128,7 @@ func resolveLatestSkillsReleaseRef(ctx context.Context) (ref string, explicit bo return "v" + embedded, false, nil } if embeddedErr != nil { - return "", false, fmt.Errorf("resolve latest skills release: %w (embedded fallback failed: %v)", err, embeddedErr) + return "", false, fmt.Errorf("resolve latest skills release: %w (embedded fallback failed: %v)", err, embeddedErr) //nolint:errorlint // only the primary error belongs in the chain } return "", false, fmt.Errorf("resolve latest skills release: %w", err) } @@ -785,7 +785,7 @@ func replaceDir(destDir, tmpDir string) error { if err := renamePathFn(tmpDir, destDir); err != nil { if backupDest != "" { if restoreErr := renamePathFn(backupDest, destDir); restoreErr != nil { - return fmt.Errorf("failed to move skill into place: %w (also failed to restore existing skill: %v)", err, restoreErr) + return fmt.Errorf("failed to move skill into place: %w (also failed to restore existing skill: %v)", err, restoreErr) //nolint:errorlint // only the primary error belongs in the chain } } return fmt.Errorf("failed to move skill into place: %w", err) diff --git a/libs/dyn/jsonloader/json.go b/libs/dyn/jsonloader/json.go index 2500e5624dc..ffb715e9c9c 100644 --- a/libs/dyn/jsonloader/json.go +++ b/libs/dyn/jsonloader/json.go @@ -30,7 +30,7 @@ func LoadJSON(data []byte, source string) (dyn.Value, error) { // Get the current offset for error reporting errorOffset := decoder.InputOffset() errorLocation := offset.GetPosition(errorOffset) - return dyn.InvalidValue, fmt.Errorf("error decoding JSON at %s: %v", errorLocation, err) + return dyn.InvalidValue, fmt.Errorf("error decoding JSON at %s: %w", errorLocation, err) } // Check if there are any remaining tokens (should not be valid JSON) diff --git a/libs/sync/snapshot.go b/libs/sync/snapshot.go index f8454d3c1d4..f936ae197d2 100644 --- a/libs/sync/snapshot.go +++ b/libs/sync/snapshot.go @@ -93,7 +93,7 @@ func SnapshotPath(opts *SyncOptions) (string, error) { if _, err := os.Stat(snapshotDir); errors.Is(err, fs.ErrNotExist) { err = os.MkdirAll(snapshotDir, 0o755) if err != nil { - return "", fmt.Errorf("failed to create config directory: %s", err) + return "", fmt.Errorf("failed to create config directory: %w", err) } } fileName := GetFileName(opts.Host, opts.RemotePath) @@ -125,10 +125,10 @@ func (s *Snapshot) Save(ctx context.Context) error { // persist snapshot to disk bytes, err := json.MarshalIndent(s, "", " ") if err != nil { - return fmt.Errorf("failed to json marshal in-memory snapshot: %s", err) + return fmt.Errorf("failed to json marshal in-memory snapshot: %w", err) } if err := atomicfile.Write(s.snapshotPath, bytes, 0o644); err != nil { - return fmt.Errorf("failed to write sync snapshot to disk: %s", err) + return fmt.Errorf("failed to write sync snapshot to disk: %v", err) //nolint:errorlint // files.Upload treats fs.ErrPermission as a workspace permission problem; this is a local file } return nil } @@ -146,13 +146,13 @@ func loadOrNewSnapshot(ctx context.Context, opts *SyncOptions) (*Snapshot, error bytes, err := os.ReadFile(snapshot.snapshotPath) if err != nil { - return nil, fmt.Errorf("failed to read sync snapshot from disk: %s", err) + return nil, fmt.Errorf("failed to read sync snapshot from disk: %w", err) } var fromDisk Snapshot err = json.Unmarshal(bytes, &fromDisk) if err != nil { - return nil, fmt.Errorf("failed to json unmarshal persisted snapshot: %s", err) + return nil, fmt.Errorf("failed to json unmarshal persisted snapshot: %w", err) } // invalidate old snapshot with schema versions @@ -164,7 +164,7 @@ func loadOrNewSnapshot(ctx context.Context, opts *SyncOptions) (*Snapshot, error // unmarshal again over the existing snapshot instance err = json.Unmarshal(bytes, &snapshot) if err != nil { - return nil, fmt.Errorf("failed to json unmarshal persisted snapshot: %s", err) + return nil, fmt.Errorf("failed to json unmarshal persisted snapshot: %w", err) } // Ensure that all paths are slash-separated upon loading diff --git a/libs/telemetry/logger.go b/libs/telemetry/logger.go index 6f366463a7a..7c57b87f520 100644 --- a/libs/telemetry/logger.go +++ b/libs/telemetry/logger.go @@ -98,7 +98,7 @@ func Upload(ctx context.Context, ec protos.ExecutionContext) error { for i, log := range l.logs { b, err := json.Marshal(log) if err != nil { - return fmt.Errorf("failed to marshal log: %s", err) + return fmt.Errorf("failed to marshal log: %w", err) } protoLogs[i] = string(b) } diff --git a/libs/testserver/jobs.go b/libs/testserver/jobs.go index a004cae7151..772d868cb14 100644 --- a/libs/testserver/jobs.go +++ b/libs/testserver/jobs.go @@ -800,7 +800,7 @@ func (s *FakeWorkspace) executePythonWheelTask(jobSettings *jobs.JobSettings, ta installArgs := []string{"pip", "install", "-q", "--python", venvPython(env.venvDir)} installArgs = append(installArgs, newWhlPaths...) if out, err := exec.Command("uv", installArgs...).CombinedOutput(); err != nil { - return "", fmt.Errorf("uv pip install failed: %s\n%s", err, out) + return "", fmt.Errorf("uv pip install failed: %w\n%s", err, out) } } @@ -877,7 +877,7 @@ func (s *FakeWorkspace) executeNotebookTask(task jobs.Task, notebookParams map[s venvDir := filepath.Join(tmpDir, ".venv") uvArgs := []string{"venv", "-q", "--python", pythonVersion, venvDir} if out, err := exec.Command("uv", uvArgs...).CombinedOutput(); err != nil { - return "", fmt.Errorf("uv venv failed: %s\n%s", err, out) + return "", fmt.Errorf("uv venv failed: %w\n%s", err, out) } // Install wheels from %pip commands @@ -899,7 +899,7 @@ func (s *FakeWorkspace) executeNotebookTask(task jobs.Task, notebookParams map[s installArgs := []string{"pip", "install", "-q", "--python", venvPython(venvDir)} installArgs = append(installArgs, localWhlPaths...) if out, err := exec.Command("uv", installArgs...).CombinedOutput(); err != nil { - return "", fmt.Errorf("uv pip install failed: %s\n%s", err, out) + return "", fmt.Errorf("uv pip install failed: %w\n%s", err, out) } } @@ -989,7 +989,7 @@ func (s *FakeWorkspace) getOrCreateClusterEnv(task jobs.Task) (*clusterEnv, func uvArgs := []string{"venv", "-q", "--python", pythonVersion, venvDir} if out, err := exec.Command("uv", uvArgs...).CombinedOutput(); err != nil { os.RemoveAll(tmpDir) - return nil, nil, fmt.Errorf("uv venv failed: %s\n%s", err, out) + return nil, nil, fmt.Errorf("uv venv failed: %w\n%s", err, out) } env := &clusterEnv{