|
| 1 | +# Metacoder Goose Coder Fixes |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Fixed systematic failures in Goose evaluations (100% failure rate with exit code 101) by properly configuring environment variables and adding error handling to the GooseCoder implementation. |
| 6 | + |
| 7 | +## Problem |
| 8 | + |
| 9 | +Goose evaluations were failing immediately with: |
| 10 | +- Exit code 101 |
| 11 | +- Execution time: 0.02-0.06 seconds (indicating immediate crash) |
| 12 | +- 100% failure rate across all 100 tests |
| 13 | + |
| 14 | +## Root Causes |
| 15 | + |
| 16 | +1. **Missing environment variable configuration**: Goose requires `GOOSE_PROVIDER__API_KEY` (double underscore) but metacoder was not setting it |
| 17 | +2. **Config precedence issues**: Global `~/.config/goose/config.yaml` settings were overriding local evaluation configs |
| 18 | +3. **Missing error handling**: Process failures crashed the entire evaluation instead of marking individual tests as failed |
| 19 | +4. **Problematic global extensions**: Global config had extensions enabled that didn't exist in evaluation workdirs |
| 20 | + |
| 21 | +## Solutions Implemented |
| 22 | + |
| 23 | +### 1. Environment Variable Configuration |
| 24 | + |
| 25 | +**File**: `.venv/lib/python3.10/site-packages/metacoder/coders/goose.py` |
| 26 | + |
| 27 | +**Location**: Lines 193-217 in the `run()` method |
| 28 | + |
| 29 | +**Changes**: |
| 30 | +```python |
| 31 | +# Override provider and model from config via environment variables |
| 32 | +# This ensures local settings take precedence over global config |
| 33 | +if self.config and self.config.ai_model: |
| 34 | + model = self.config.ai_model |
| 35 | + # Get provider as string |
| 36 | + if isinstance(model.provider, str): |
| 37 | + provider_str = model.provider |
| 38 | + elif model.provider and hasattr(model.provider, "name"): |
| 39 | + provider_str = model.provider.name |
| 40 | + else: |
| 41 | + provider_str = "openai" # default |
| 42 | + |
| 43 | + env["GOOSE_PROVIDER"] = provider_str |
| 44 | + env["GOOSE_MODEL"] = model.name |
| 45 | + logger.debug(f"Setting GOOSE_PROVIDER={provider_str}, GOOSE_MODEL={model.name}") |
| 46 | + |
| 47 | + # Set API key via GOOSE_PROVIDER__API_KEY environment variable |
| 48 | + # Check for provider-specific API keys in environment |
| 49 | + if provider_str == "anthropic" and "ANTHROPIC_API_KEY" in env: |
| 50 | + env["GOOSE_PROVIDER__API_KEY"] = env["ANTHROPIC_API_KEY"] |
| 51 | + logger.debug("Set GOOSE_PROVIDER__API_KEY from ANTHROPIC_API_KEY") |
| 52 | + elif provider_str == "openai" and "OPENAI_API_KEY" in env: |
| 53 | + env["GOOSE_PROVIDER__API_KEY"] = env["OPENAI_API_KEY"] |
| 54 | + logger.debug("Set GOOSE_PROVIDER__API_KEY from OPENAI_API_KEY") |
| 55 | +``` |
| 56 | + |
| 57 | +**Why this fixes the issue**: |
| 58 | +- Goose uses `GOOSE_PROVIDER__API_KEY` (double underscore) as the standard API key environment variable |
| 59 | +- Setting `GOOSE_PROVIDER` and `GOOSE_MODEL` as environment variables ensures they override global config |
| 60 | +- Maps provider-specific keys (ANTHROPIC_API_KEY, OPENAI_API_KEY) to Goose's expected format |
| 61 | + |
| 62 | +### 2. Error Handling |
| 63 | + |
| 64 | +**File**: `.venv/lib/python3.10/site-packages/metacoder/coders/goose.py` |
| 65 | + |
| 66 | +**Location**: Lines 223-239 in the `run()` method |
| 67 | + |
| 68 | +**Changes**: |
| 69 | +```python |
| 70 | +try: |
| 71 | + result = self.run_process(command, env) |
| 72 | + end_time = time.time() |
| 73 | + ao = CoderOutput(stdout=result.stdout, stderr=result.stderr) |
| 74 | + logger.info(f"🦆 Command took {end_time - start_time:.2f} seconds") |
| 75 | +except Exception as e: |
| 76 | + # Handle process errors gracefully - don't crash entire evaluation |
| 77 | + end_time = time.time() |
| 78 | + logger.warning(f"Goose command failed (test will be marked as failed): {e}") |
| 79 | + ao = CoderOutput( |
| 80 | + stdout="", |
| 81 | + stderr=str(e), |
| 82 | + result_text=f"Error: {str(e)}", |
| 83 | + success=False, |
| 84 | + ) |
| 85 | + logger.info(f"🦆 Command took {end_time - start_time:.2f} seconds") |
| 86 | + return ao |
| 87 | +``` |
| 88 | + |
| 89 | +**Why this fixes the issue**: |
| 90 | +- Prevents single test failures from crashing the entire evaluation |
| 91 | +- Gracefully handles process errors and marks individual tests as failed |
| 92 | +- Allows evaluation to continue through all 100 tests |
| 93 | + |
| 94 | +### 3. Global Config Fixes |
| 95 | + |
| 96 | +**File**: `~/.config/goose/config.yaml` |
| 97 | + |
| 98 | +**Change**: Disabled problematic `tabfilequery` extension |
| 99 | + |
| 100 | +```yaml |
| 101 | +tabfilequery: |
| 102 | + enabled: false # Changed from true |
| 103 | +``` |
| 104 | +
|
| 105 | +**Why this was needed**: |
| 106 | +- The global config had `tabfilequery` extension enabled |
| 107 | +- This extension tried to run `src/fitness_mcp/main.py` which doesn't exist in evaluation workdirs |
| 108 | +- Caused Goose to fail on startup |
| 109 | + |
| 110 | +## Results |
| 111 | + |
| 112 | +**Before fixes**: |
| 113 | +- 100/100 tests failing (exit code 101) |
| 114 | +- Test duration: 0.02-0.06 seconds (immediate crash) |
| 115 | +- No results generated |
| 116 | + |
| 117 | +**After fixes**: |
| 118 | +- 100/100 tests completed successfully (exit code 0) |
| 119 | +- Test duration: 10-24 seconds (proper execution) |
| 120 | +- Results file generated: `goose_20251101.yaml` (919KB, 18,595 lines) |
| 121 | +- Average score: 0.137 (~13.7%) |
| 122 | + |
| 123 | +## Files Modified |
| 124 | + |
| 125 | +1. **`.venv/lib/python3.10/site-packages/metacoder/coders/goose.py`** |
| 126 | + - Added environment variable configuration (lines 193-217) |
| 127 | + - Added error handling (lines 223-239) |
| 128 | + |
| 129 | +2. **`~/.config/goose/config.yaml`** |
| 130 | + - Disabled `tabfilequery` extension (line 132) |
| 131 | + |
| 132 | +## Testing |
| 133 | + |
| 134 | +Verified with: |
| 135 | +```bash |
| 136 | +export OPENAI_API_KEY=$(cat ~/openai.key) |
| 137 | +export ANTHROPIC_API_KEY=$(cat ~/anthropic.key) |
| 138 | +uv run metacoder eval project/literature_mcp_eval_config.yaml -o results/raw/goose_20251101.yaml |
| 139 | +``` |
| 140 | + |
| 141 | +Result: All 100 tests completed successfully. |
| 142 | + |
| 143 | +### Error 4: MCP extensions not loading (CRITICAL) |
| 144 | + |
| 145 | +**Problem**: All 100 Goose tests showed "No extensions available to enable" even though extensions were configured in `.config/goose/config.yaml`. |
| 146 | + |
| 147 | +**Root cause discovered**: `goose run` command DOES NOT load extensions from config files. Extensions must be passed as command-line arguments using `--with-extension`. |
| 148 | + |
| 149 | +**Fix**: Modified `goose.py` lines 221-232 to add `--with-extension` flags for each configured MCP extension: |
| 150 | + |
| 151 | +```python |
| 152 | +# Add MCP extensions as command-line arguments |
| 153 | +# Config files alone don't work with 'goose run' - must use --with-extension |
| 154 | +if self.config and self.config.extensions: |
| 155 | + for mcp in self.config.extensions: |
| 156 | + if isinstance(mcp, MCPConfig) and mcp.enabled: |
| 157 | + # Build extension command from cmd + args |
| 158 | + if mcp.command: |
| 159 | + ext_cmd = mcp.command |
| 160 | + if mcp.args: |
| 161 | + ext_cmd = ext_cmd + " " + " ".join(mcp.args) |
| 162 | + command.extend(["--with-extension", ext_cmd]) |
| 163 | + logger.debug(f"Adding extension: --with-extension {ext_cmd}") |
| 164 | +``` |
| 165 | + |
| 166 | +**Example**: For an extension configured as: |
| 167 | +```yaml |
| 168 | +extensions: |
| 169 | + artl: |
| 170 | + cmd: uvx |
| 171 | + args: |
| 172 | + - artl-mcp |
| 173 | +``` |
| 174 | + |
| 175 | +The command now becomes: |
| 176 | +```bash |
| 177 | +goose run -t "text" --with-extension "uvx artl-mcp" |
| 178 | +``` |
| 179 | + |
| 180 | +**Verification**: |
| 181 | +- Before fix: "I don't currently have access to any extensions" |
| 182 | +- After fix: Goose successfully calls MCP tools like `get_paper`, `get_metadata`, etc. |
| 183 | + |
| 184 | +**Reference**: https://block.github.io/goose/docs/guides/goose-cli-commands/ documents the `--with-extension` flag. |
| 185 | + |
| 186 | +## Upstream Contribution |
| 187 | + |
| 188 | +The changes to `goose.py` should be contributed back to the metacoder project as they fix critical bugs that prevent Goose evaluations from working: |
| 189 | + |
| 190 | +1. Missing `GOOSE_PROVIDER__API_KEY` environment variable configuration |
| 191 | +2. MCP extensions not being passed to `goose run` command via `--with-extension` flags |
| 192 | +3. Crash-on-error bug that prevented graceful failure handling |
| 193 | + |
| 194 | +### Patch File |
| 195 | + |
| 196 | +A patch file has been created: `metacoder_goose_error_handling.patch` |
| 197 | + |
| 198 | +### Recommended Next Steps |
| 199 | + |
| 200 | +1. Submit a PR to the metacoder repository with these fixes |
| 201 | +2. Include all three fixes: environment variables, extension command-line args, and error handling |
| 202 | +3. Add documentation about: |
| 203 | + - Goose's environment variable requirements (`GOOSE_PROVIDER__API_KEY`) |
| 204 | + - How `goose run` requires `--with-extension` flags (config files are not sufficient) |
0 commit comments