opencode
opencode copied to clipboard
ProviderModelNotFoundError for custom provider in GitHub Action (worked until 07 Jan)
Description
Custom Provider Configuration Not Loading - ProviderModelNotFoundError
Description
Custom provider configuration stopped working after 07 Jan 2025. The same workflow and config.json that previously worked now fails with ProviderModelNotFoundError, suggesting the custom provider definition is not being loaded.
Error Message
ProviderModelNotFoundError: ProviderModelNotFoundError
data: {
providerID: "zhipuai",
modelID: "glm-4.7",
suggestions: [],
}
Note: suggestions: [] indicates OpenCode cannot find any custom providers, not just the specific model.
Environment
- OpenCode Version:
@latest(via GitHub Actionanomalyco/opencode/github@latest) - Runner:
blacksmith-2vcpu-ubuntu-2404-arm(Ubuntu 24.04 ARM64) - Node.js: 20
- Last Working: 07 Jan 2025
Configuration
config.json:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"zhipuai": {
"npm": "@ai-sdk/openai-compatible",
"name": "ZhipuAI GLM",
"options": {
"baseURL": "https://open.bigmodel.cn/api/coding/paas/v4",
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"glm-4.7": {
"name": "GLM-4.7",
"limit": {
"context": 128000,
"output": 4096
}
}
}
}
}
}
GitHub Action Usage:
- name: Fix error with OpenCode
uses: anomalyco/opencode/github@latest
env:
OPENAI_API_KEY: ${{ secrets.GLM_API_KEY }}
with:
model: zhipuai/glm-4.7
prompt: |
...
Steps to Reproduce
- Create a GitHub workflow using
anomalyco/opencode/github@latest - Add a custom provider in
config.json(as shown above) - Set
model: zhipuai/glm-4.7in the action inputs - Trigger the workflow
Expected Behavior
OpenCode should load the custom provider configuration from config.json and use the specified model.
Actual Behavior
OpenCode throws ProviderModelNotFoundError with empty suggestions, indicating the custom provider is not recognized at all.
Full Workflow Output
Successfully added opencode to $PATH in /home/runner/.bashrc
Added /home/runner/.opencode/bin to $GITHUB_PATH
...
Run opencode github run
Configuring git...
AUTHORIZATION: basic ***
opencode session ses_44e86514dffeRLPXnw7DhrAMMy
Triggered by: florian583
Checking out new branch...
Switched to a new branch 'opencode/dispatch-e174bf-20260112091140'
3ba3dfa1ea586d64bbfa474f3d7db6010b12a837
Sending message to opencode...
984 | const provider = s.providers[providerID]
985 | if (!provider) {
986 | const availableProviders = Object.keys(s.providers)
987 | const matches = fuzzysort.go(providerID, availableProviders, { limit: 3, threshold: -10000 })
988 | const suggestions = matches.map((m) => m.target)
989 | throw new ModelNotFoundError({ providerID, modelID, suggestions })
^
ProviderModelNotFoundError: ProviderModelNotFoundError
data: {
providerID: "zhipuai",
modelID: "glm-4.7",
suggestions: [],
},
at getModel (src/provider/provider.ts:989:13)
Error: ProviderModelNotFoundError
Error: Process completed with exit code 1.
Questions
- Did the config loading behavior change after 07 Jan?
- Is
config.jsonstill the correct location for custom provider definitions in GitHub Actions? - Should there be additional steps to register custom providers in the GitHub Action context?
- Version pinning: How can I pin a specific version of the GitHub Action and OpenCode binary to avoid breaking changes? Is there a recommended way to lock versions (e.g.,
anomalyco/opencode/[email protected]instead of@latest)? - Custom provider setup: What is the recommended way to configure custom OpenAI-compatible providers? Is there updated documentation for the
config.jsonschema, especially for providers using@ai-sdk/openai-compatible?
Workflow File
Full workflow file (.github/workflows/opencode-error-fixer.yml)
name: OpenCode Error Fixer
on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
inputs:
environment:
description: "Environment to scan"
required: true
type: choice
default: "prod"
options:
- prod
- dev
- all
max_fixes:
description: "Maximum number of fixes to attempt"
required: false
type: choice
default: "5"
options:
- "1"
- "3"
- "5"
- "10"
env:
NODE_VERSION: "20"
jobs:
collect-errors:
runs-on: blacksmith-2vcpu-ubuntu-2404-arm
outputs:
matrix: ${{ steps.triage-filter.outputs.matrix || steps.triage-fallback.outputs.matrix }}
has_errors: ${{ steps.triage-filter.outputs.has_errors || steps.triage-fallback.outputs.has_errors }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install CLI tools
run: |
sudo apt-get update
sudo apt-get install -y jq
npm install -g @sentry/cli
AXIOM_VERSION=$(curl -s https://api.github.com/repos/axiomhq/cli/releases/latest | jq -r '.tag_name')
VERSION_NUM=${AXIOM_VERSION#v}
curl -sL "https://github.com/axiomhq/cli/releases/download/${AXIOM_VERSION}/axiom_${VERSION_NUM}_linux_arm64.tar.gz" -o axiom.tar.gz
tar -xzf axiom.tar.gz
sudo mv "axiom_${VERSION_NUM}_linux_arm64/axiom" /usr/local/bin/axiom
rm -rf axiom.tar.gz "axiom_${VERSION_NUM}_linux_arm64"
- name: Install script dependencies
run: |
cd .github/scripts/ai-error-fixer
npm install
- name: Collect and group errors
id: group
env:
GH_TOKEN: ${{ github.token }}
SENTRY_AUTH_TOKEN: ${{ secrets.AI_FIXER_SENTRY_AUTH_TOKEN }}
AXIOM_TOKEN: ${{ secrets.AI_FIXER_AXIOM_TOKEN }}
AXIOM_ORG_ID: ${{ secrets.AI_FIXER_AXIOM_ORG_ID }}
ENVIRONMENT_FILTER: ${{ github.event.inputs.environment || 'prod' }}
MAX_FIXES: ${{ github.event.inputs.max_fixes || '3' }}
run: |
cd .github/scripts/ai-error-fixer
node collect-errors.mjs
if [ -f /tmp/errors.json ]; then
jq -r --argjson max "$MAX_FIXES" '
group_by(.file) |
map({
file: .[0].file,
title: .[0].title,
total_frequency: (map(.frequency) | add),
sentry_links: [.[] | .sentryLink // .axiomLink] | unique,
error_ids: [.[] | .id],
name: (
(.[0].file // "unknown" | gsub("[^a-zA-Z0-9]"; "-") | gsub("-+"; "-") | gsub("^-|-$"; "") | .[0:30]) +
"-" +
(.[0].title // "error" | gsub("[^a-zA-Z0-9 ]"; "") | split(" ") | .[0:3] | join("-") | ascii_downcase | .[0:20])
)
}) |
sort_by(-.total_frequency) |
.[:$max]
' /tmp/errors.json > /tmp/grouped-errors.json
COUNT=$(jq length /tmp/grouped-errors.json)
echo "Found $COUNT unique error groups before AI triage"
echo "grouped_count=$COUNT" >> $GITHUB_OUTPUT
if [ "$COUNT" -gt 0 ]; then
cp /tmp/grouped-errors.json "$GITHUB_WORKSPACE/grouped-errors.json"
echo "has_grouped_errors=true" >> $GITHUB_OUTPUT
else
echo "has_grouped_errors=false" >> $GITHUB_OUTPUT
fi
else
echo "has_grouped_errors=false" >> $GITHUB_OUTPUT
fi
- name: Fetch open PRs for triage
if: steps.group.outputs.has_grouped_errors == 'true'
id: fetch-prs
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr list --state open --label "ai-fix" --json number,title,body,createdAt --limit 50 > "$GITHUB_WORKSPACE/open-prs.json" || echo "[]" > "$GITHUB_WORKSPACE/open-prs.json"
echo "Open PRs with ai-fix label:"
cat "$GITHUB_WORKSPACE/open-prs.json" | jq 'length'
- name: Read data for triage
if: steps.group.outputs.has_grouped_errors == 'true'
id: read-triage-data
run: |
{
echo 'errors<<ERRORS_EOF'
cat "$GITHUB_WORKSPACE/grouped-errors.json"
echo 'ERRORS_EOF'
} >> $GITHUB_OUTPUT
{
echo 'prs<<PRS_EOF'
cat "$GITHUB_WORKSPACE/open-prs.json"
echo 'PRS_EOF'
} >> $GITHUB_OUTPUT
- name: AI-based duplicate PR detection
if: steps.group.outputs.has_grouped_errors == 'true'
uses: vercel/ai-action@v2
id: triage
env:
ERROR_GROUPS: ${{ steps.read-triage-data.outputs.errors }}
OPEN_PRS: ${{ steps.read-triage-data.outputs.prs }}
with:
model: "minimax/minimax-m2.1"
api-key: ${{ secrets.AI_GATEWAY_API_KEY }}
system: "You are a production error triage system. Match errors to existing PRs intelligently."
schema: |
{
"type": "object",
"properties": {
"triage_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"should_process": {"type": "boolean"},
"matching_pr": {"type": "number"},
"reason": {"type": "string"}
},
"required": ["name", "should_process", "reason"]
}
}
},
"required": ["triage_results"]
}
prompt: |
Compare these error groups against existing open PRs to prevent duplicates.
ERROR GROUPS TO TRIAGE:
${{ env.ERROR_GROUPS }}
OPEN PRs WITH AI-FIX LABEL:
${{ env.OPEN_PRS }}
For each error group, determine if it's already covered by an open PR:
1. Match by Sentry issue ID in PR body
2. Match by file path and error message similarity
3. Match by root cause description
4. Consider PR age (PRs >14 days old likely stale - can reprocess)
For each error group, return:
- name: The error group name
- should_process: false if covered by open PR, true otherwise
- matching_pr: PR number if match found (optional)
- reason: Brief explanation
- name: Filter errors based on AI triage
id: triage-filter
if: steps.triage.outcome == 'success'
env:
TRIAGE_JSON: ${{ steps.triage.outputs.json }}
run: |
npm install -g jsonrepair
FIXED_TRIAGE=$(printf '%s' "$TRIAGE_JSON" | jsonrepair)
echo "AI Triage Results:"
echo "$FIXED_TRIAGE" | jq .
FILTERED=$(jq --argjson triage "$FIXED_TRIAGE" '
[.[] | . as $err |
($triage.triage_results[] | select(.name == $err.name and .should_process == true)) as $match |
if $match then $err else empty end]
' "$GITHUB_WORKSPACE/grouped-errors.json")
echo "After AI triage: $(echo "$FILTERED" | jq length) error groups to process"
COUNT=$(echo "$FILTERED" | jq length)
if [ "$COUNT" -gt 0 ]; then
MATRIX=$(echo "$FILTERED" | jq -c '{include: .}')
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
echo "has_errors=true" >> $GITHUB_OUTPUT
else
echo "has_errors=false" >> $GITHUB_OUTPUT
fi
- name: Fallback if AI triage fails
id: triage-fallback
if: steps.triage.outcome != 'success' && steps.group.outputs.has_grouped_errors == 'true'
run: |
echo "AI triage failed, using all grouped errors"
MATRIX=$(jq -c '{include: .}' "$GITHUB_WORKSPACE/grouped-errors.json")
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
echo "has_errors=true" >> $GITHUB_OUTPUT
fix-error:
name: fix-${{ matrix.name }}
needs: collect-errors
if: needs.collect-errors.outputs.has_errors == 'true'
runs-on: blacksmith-2vcpu-ubuntu-2404-arm
strategy:
fail-fast: false
max-parallel: 10
matrix: ${{ fromJson(needs.collect-errors.outputs.matrix) }}
permissions:
id-token: write
contents: write
pull-requests: write
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install CLI tools
run: |
sudo apt-get update
sudo apt-get install -y jq postgresql-client
npm install -g @sentry/cli
AXIOM_VERSION=$(curl -s https://api.github.com/repos/axiomhq/cli/releases/latest | jq -r '.tag_name')
VERSION_NUM=${AXIOM_VERSION#v}
curl -sL "https://github.com/axiomhq/cli/releases/download/${AXIOM_VERSION}/axiom_${VERSION_NUM}_linux_arm64.tar.gz" -o axiom.tar.gz
tar -xzf axiom.tar.gz
sudo mv "axiom_${VERSION_NUM}_linux_arm64/axiom" /usr/local/bin/axiom
rm -rf axiom.tar.gz "axiom_${VERSION_NUM}_linux_arm64"
- name: Configure git for pushing
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git remote set-url origin https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git
echo ".github/" >> .git/info/exclude
- name: Create error file for this fix
run: |
cat > error.json << 'EOF'
${{ toJson(matrix) }}
EOF
echo "Fixing error group:"
jq . error.json
- name: Fix error with OpenCode
uses: anomalyco/opencode/github@latest
env:
GH_TOKEN: ${{ github.token }}
OPENAI_API_KEY: ${{ secrets.GLM_API_KEY }}
API_TIMEOUT_MS: "1800000"
SENTRY_AUTH_TOKEN: ${{ secrets.AI_FIXER_SENTRY_AUTH_TOKEN }}
AXIOM_TOKEN: ${{ secrets.AI_FIXER_AXIOM_TOKEN }}
AXIOM_ORG_ID: ${{ secrets.AI_FIXER_AXIOM_ORG_ID }}
READONLY_DEV_DB_URL: ${{ secrets.READONLY_DEV_DB_URL }}
READONLY_PROD_DB_URL: ${{ secrets.READONLY_PROD_DB_URL }}
with:
model: zhipuai/glm-4.7
prompt: |
YOU ARE AN AUTOMATED BUG FIXER. Fix ONE specific error and OpenCode will create a PR.
Setup: Read AGENTS.md + .cursor/rules/code-quality.mdc for project conventions.
YOUR TASK: Read error.json - it contains ONE error group to fix.
- file: ${{ matrix.file }}
- title: ${{ matrix.title }}
- frequency: ${{ matrix.total_frequency }}
- sentry_links: See error.json for full list
CLI Tools available (for investigation only):
- sentry-cli: `sentry-cli issues list --project prod-api-ats`
- axiom: `axiom query "['prod-api-ats'] | where _time > ago(1h)" --format=json`
- psql: `psql "$READONLY_DEV_DB_URL" -c "SELECT * FROM ..."`
STEPS:
1. ANALYZE: Read the affected file and related code (use Read, Grep tools)
2. FIX: Make targeted code changes (use Edit tool to modify files)
3. VERIFY: Run `bun run format && bun run type-check`
4. DONE: Print summary - OpenCode wrapper will handle commit/push/PR
If this is an INFRA issue (not fixable in code):
Create a GitHub issue: `gh issue create --title "..." --body "..." --label "bug"`
Then stop - do not try to fix infra issues in code.
CRITICAL RULES:
- FIX CODE ONLY - make file edits, do NOT use git commands
- DO NOT run: git checkout, git commit, git push, git branch
- DO NOT create branches or PRs - OpenCode wrapper handles this automatically
- NEVER modify .github/ directory
- Run type-check before finishing: `bun run format && bun run type-check`
- After fixing, print a summary - OpenCode will auto-commit and create PR
- name: Label PR and create tracking issue
if: always()
env:
GH_TOKEN: ${{ github.token }}
ERROR_TITLE: ${{ matrix.title }}
ERROR_FILE: ${{ matrix.file }}
ERROR_FREQ: ${{ matrix.total_frequency }}
ERROR_NAME: ${{ matrix.name }}
SENTRY_JSON: ${{ toJson(matrix.sentry_links) }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
gh label create "ai-fix" --description "Automated fix by OpenCode" --color "0E8A16" --force 2>/dev/null || true
CURRENT_BRANCH=$(git branch --show-current)
echo "Current branch: $CURRENT_BRANCH"
PR_DATA=""
for i in {1..6}; do
sleep 5
PR_DATA=$(gh pr list --head "$CURRENT_BRANCH" --state open --json number,url,labels --jq '.[0] // empty')
if [ -n "$PR_DATA" ]; then
echo "Found PR after $((i * 5)) seconds"
break
fi
echo "Attempt $i: PR not found yet, waiting..."
done
if [ -n "$PR_DATA" ]; then
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number')
PR_URL=$(echo "$PR_DATA" | jq -r '.url')
echo "Found PR #$PR_NUMBER: $PR_URL"
echo "Adding ai-fix label to PR #$PR_NUMBER"
gh pr edit "$PR_NUMBER" --add-label "ai-fix"
sleep 2
LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name' | tr '\n' ', ')
echo "PR labels: $LABELS"
else
echo "WARNING: No PR found for branch $CURRENT_BRANCH after 30 seconds"
PR_URL=""
fi
SENTRY_LINKS=$(echo "$SENTRY_JSON" | jq -r '.[]? // empty' | head -5 | sed 's/^/- /')
cat > /tmp/issue-body.md << EOF
## OpenCode Error Fixer Run
Error: ${ERROR_TITLE}
File: ${ERROR_FILE}
Frequency: ${ERROR_FREQ}
### Sentry Links
${SENTRY_LINKS:-No links available}
### PR
${PR_URL:-No PR created - check workflow run}
### Workflow Run
${RUN_URL}
EOF
gh issue create --title "[OpenCode Fix] ${ERROR_NAME}" --label "bug" --body-file /tmp/issue-body.md || echo "Failed to create issue"
Plugins
@ai-sdk/openai-compatible
OpenCode version
latest
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response