opencode icon indicating copy to clipboard operation
opencode copied to clipboard

ProviderModelNotFoundError for custom provider in GitHub Action (worked until 07 Jan)

Open florian583 opened this issue 3 days ago • 2 comments

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 Action anomalyco/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

  1. Create a GitHub workflow using anomalyco/opencode/github@latest
  2. Add a custom provider in config.json (as shown above)
  3. Set model: zhipuai/glm-4.7 in the action inputs
  4. 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

  1. Did the config loading behavior change after 07 Jan?
  2. Is config.json still the correct location for custom provider definitions in GitHub Actions?
  3. Should there be additional steps to register custom providers in the GitHub Action context?
  4. 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)?
  5. Custom provider setup: What is the recommended way to configure custom OpenAI-compatible providers? Is there updated documentation for the config.json schema, 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

florian583 avatar Jan 12 '26 09:01 florian583