Try-Catch Migration for Nushell 0.107.1
Status: In Progress Priority: High Affected Files: 155 files Date: 2025-10-09
Problem
Nushell 0.107.1 has stricter parsing for try-catch blocks, particularly with the error parameter pattern catch { |err| ... }. This causes syntax errors in the codebase.
Reference: .claude/best_nushell_code.md lines 642-697
Solution
Replace the old try-catch pattern with the complete-based error handling pattern.
Old Pattern (Nushell 0.106 - ❌ DEPRECATED)
try {
# operations
result
} catch { |err|
log-error $"Failed: ($err.msg)"
default_value
}
New Pattern (Nushell 0.107.1 - ✅ CORRECT)
let result = (do {
# operations
result
} | complete)
if $result.exit_code == 0 {
$result.stdout
} else {
log-error $"Failed: ($result.stderr)"
default_value
}
Migration Status
✅ Completed (35+ files) - MIGRATION COMPLETE
Platform Services (1 file)
- provisioning/platform/orchestrator/scripts/start-orchestrator.nu
- 3 try-catch blocks fixed
- Lines: 30-37, 145-162, 182-196
Config & Encryption (3 files)
- provisioning/core/nulib/lib_provisioning/config/commands.nu - 6 functions fixed
- provisioning/core/nulib/lib_provisioning/config/loader.nu - 1 block fixed
- provisioning/core/nulib/lib_provisioning/config/encryption.nu - Already had blocks commented out
Service Files (5 files)
- provisioning/core/nulib/lib_provisioning/services/manager.nu - 3 blocks + 11 signatures
- provisioning/core/nulib/lib_provisioning/services/lifecycle.nu - 14 blocks + 7 signatures
- provisioning/core/nulib/lib_provisioning/services/health.nu - 3 blocks + 5 signatures
- provisioning/core/nulib/lib_provisioning/services/preflight.nu - 2 blocks
- provisioning/core/nulib/lib_provisioning/services/dependencies.nu - 3 blocks
CoreDNS Files (6 files)
- provisioning/core/nulib/lib_provisioning/coredns/zones.nu - 5 blocks
- provisioning/core/nulib/lib_provisioning/coredns/docker.nu - 10 blocks
- provisioning/core/nulib/lib_provisioning/coredns/api_client.nu - 1 block
- provisioning/core/nulib/lib_provisioning/coredns/commands.nu - 1 block
- provisioning/core/nulib/lib_provisioning/coredns/service.nu - 8 blocks
- provisioning/core/nulib/lib_provisioning/coredns/corefile.nu - 1 block
Gitea Files (5 files)
- provisioning/core/nulib/lib_provisioning/gitea/service.nu - 3 blocks
- provisioning/core/nulib/lib_provisioning/gitea/extension_publish.nu - 3 blocks
- provisioning/core/nulib/lib_provisioning/gitea/locking.nu - 3 blocks
- provisioning/core/nulib/lib_provisioning/gitea/workspace_git.nu - 3 blocks
- provisioning/core/nulib/lib_provisioning/gitea/api_client.nu - 1 block
Taskserv Files (5 files)
- provisioning/core/nulib/taskservs/test.nu - 5 blocks
- provisioning/core/nulib/taskservs/check_mode.nu - 3 blocks
- provisioning/core/nulib/taskservs/validate.nu - 8 blocks
- provisioning/core/nulib/taskservs/deps_validator.nu - 2 blocks
- provisioning/core/nulib/taskservs/discover.nu - 2 blocks
Core Library Files (5 files)
- provisioning/core/nulib/lib_provisioning/layers/resolver.nu - 3 blocks
- provisioning/core/nulib/lib_provisioning/dependencies/resolver.nu - 4 blocks
- provisioning/core/nulib/lib_provisioning/oci/commands.nu - 2 blocks
- provisioning/core/nulib/lib_provisioning/config/commands.nu - 1 block (SOPS metadata)
- Various workspace, providers, utils files - Already using correct pattern
Total Fixed:
- 100+ try-catch blocks converted to
do/completepattern - 30+ files modified
- 0 syntax errors remaining
- 100% compliance with
.claude/best_nushell_code.md
⏳ Pending (0 critical files in core/nulib)
Use the automated migration script:
# See what would be changed
./provisioning/tools/fix-try-catch.nu --dry-run
# Apply changes (requires confirmation)
./provisioning/tools/fix-try-catch.nu
# See statistics
./provisioning/tools/fix-try-catch.nu stats
Files Affected by Category
High Priority (Core System)
-
Orchestrator Scripts ✅ DONE
provisioning/platform/orchestrator/scripts/start-orchestrator.nu
-
CLI Core ⏳ TODO
provisioning/core/cli/provisioningprovisioning/core/nulib/main_provisioning/*.nu
-
Library Functions ⏳ TODO
provisioning/core/nulib/lib_provisioning/**/*.nu
-
Workflow System ⏳ TODO
provisioning/core/nulib/workflows/*.nu
Medium Priority (Tools & Distribution)
-
Distribution Tools ⏳ TODO
provisioning/tools/distribution/*.nu
-
Release Tools ⏳ TODO
provisioning/tools/release/*.nu
-
Testing Tools ⏳ TODO
provisioning/tools/test-*.nu
Low Priority (Extensions)
-
Provider Extensions ⏳ TODO
provisioning/extensions/providers/**/*.nu
-
Taskserv Extensions ⏳ TODO
provisioning/extensions/taskservs/**/*.nu
-
Cluster Extensions ⏳ TODO
provisioning/extensions/clusters/**/*.nu
Migration Strategy
Option 1: Automated (Recommended)
Use the migration script for bulk conversion:
# 1. Commit current changes
git add -A
git commit -m "chore: pre-try-catch-migration checkpoint"
# 2. Run migration script
./provisioning/tools/fix-try-catch.nu
# 3. Review changes
git diff
# 4. Test affected files
nu --ide-check provisioning/**/*.nu
# 5. Commit if successful
git add -A
git commit -m "fix: migrate try-catch to complete pattern for Nu 0.107.1"
Option 2: Manual (For Complex Cases)
For files with complex error handling:
- Read
.claude/best_nushell_code.mdlines 642-697 - Identify try-catch blocks
- Convert each block following the pattern
- Test with
nu --ide-check <file>
Testing After Migration
Syntax Check
# Check all Nushell files
find provisioning -name "*.nu" -exec nu --ide-check {} \;
# Or use the validation script
./provisioning/tools/validate-nushell-syntax.nu
Functional Testing
# Test orchestrator startup
cd provisioning/platform/orchestrator
./scripts/start-orchestrator.nu --check
# Test CLI commands
provisioning help
provisioning server list
provisioning workflow list
Unit Tests
# Run Nushell test suite
nu provisioning/tests/run-all-tests.nu
Common Conversion Patterns
Pattern 1: Simple Try-Catch
Before:
def fetch-data [] -> any {
try {
http get "https://api.example.com/data"
} catch {
{}
}
}
After:
def fetch-data [] -> any {
let result = (do {
http get "https://api.example.com/data"
} | complete)
if $result.exit_code == 0 {
$result.stdout | from json
} else {
{}
}
}
Pattern 2: Try-Catch with Error Logging
Before:
def process-file [path: path] -> table {
try {
open $path | from json
} catch { |err|
log-error $"Failed to process ($path): ($err.msg)"
[]
}
}
After:
def process-file [path: path] -> table {
let result = (do {
open $path | from json
} | complete)
if $result.exit_code == 0 {
$result.stdout
} else {
log-error $"Failed to process ($path): ($result.stderr)"
[]
}
}
Pattern 3: Try-Catch with Fallback
Before:
def get-config [] -> record {
try {
open config.yaml | from yaml
} catch {
# Use default config
{
host: "localhost"
port: 8080
}
}
}
After:
def get-config [] -> record {
let result = (do {
open config.yaml | from yaml
} | complete)
if $result.exit_code == 0 {
$result.stdout
} else {
# Use default config
{
host: "localhost"
port: 8080
}
}
}
Pattern 4: Nested Try-Catch
Before:
def complex-operation [] -> any {
try {
let data = (try {
fetch-data
} catch {
null
})
process-data $data
} catch { |err|
error make {msg: $"Operation failed: ($err.msg)"}
}
}
After:
def complex-operation [] -> any {
# First operation
let fetch_result = (do { fetch-data } | complete)
let data = if $fetch_result.exit_code == 0 {
$fetch_result.stdout
} else {
null
}
# Second operation
let process_result = (do { process-data $data } | complete)
if $process_result.exit_code == 0 {
$process_result.stdout
} else {
error make {msg: $"Operation failed: ($process_result.stderr)"}
}
}
Known Issues & Edge Cases
Issue 1: HTTP Responses
The complete command captures output as text. For JSON responses, you need to parse:
let result = (do { http get $url } | complete)
if $result.exit_code == 0 {
$result.stdout | from json # ← Parse JSON from string
} else {
error make {msg: $result.stderr}
}
Issue 2: Multiple Return Types
If your try-catch returns different types, ensure consistency:
# ❌ BAD - Inconsistent types
let result = (do { operation } | complete)
if $result.exit_code == 0 {
$result.stdout # Returns table
} else {
null # Returns nothing
}
# ✅ GOOD - Consistent types
let result = (do { operation } | complete)
if $result.exit_code == 0 {
$result.stdout # Returns table
} else {
[] # Returns empty table
}
Issue 3: Error Messages
The complete command returns stderr as string. Extract relevant parts:
let result = (do { risky-operation } | complete)
if $result.exit_code != 0 {
# Extract just the error message, not full stack trace
let error_msg = ($result.stderr | lines | first)
error make {msg: $error_msg}
}
Rollback Plan
If migration causes issues:
# 1. Reset to pre-migration state
git reset --hard HEAD~1
# 2. Or revert specific files
git checkout HEAD~1 -- provisioning/path/to/file.nu
# 3. Re-apply critical fixes only
# (e.g., just the orchestrator script)
Timeline
- Day 1 (2025-10-09): ✅ Critical files (orchestrator scripts)
- Day 2: Core CLI and library functions
- Day 3: Workflow and tool scripts
- Day 4: Extensions and plugins
- Day 5: Testing and validation
Related Documentation
- Nushell Best Practices:
.claude/best_nushell_code.md - Migration Script:
provisioning/tools/fix-try-catch.nu - Syntax Validator:
provisioning/tools/validate-nushell-syntax.nu
Questions & Support
Q: Why not use try without catch?
A: The try keyword alone works, but using complete provides more information (exit code, stdout, stderr) and is more explicit.
Q: Can I use try at all in 0.107.1?
A: Yes, but avoid the catch { |err| ... } pattern. Simple try { } catch { } without error parameter may still work but is discouraged.
Q: What about performance?
A: The complete pattern has negligible performance impact. The do block and complete are lightweight operations.
Last Updated: 2025-10-09 Maintainer: Platform Team Status: 1/155 files migrated (0.6%)