"Can't add same credential" when adding new model provider in a offline environment #607

Closed
opened 2026-02-16 10:19:53 -05:00 by yindo · 24 comments
Owner

Originally created by @hagemon on GitHub (Sep 1, 2025).

Self Checks

  • This is only for bug report, if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues Dify issues & Dify Official Plugins, including closed ones.
  • I confirm that I am using English to submit this report (我已阅读并同意 Language Policy).
  • [FOR CHINESE USERS] 请务必使用英文提交 Issue,否则会被关闭。谢谢!:)
  • Please do not modify this template :) and fill in all the required fields.

Dify version

1.8.0

Plugin version

vllm 0.1.5, OpenAI-API-compatible 0.0.22

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

Background

I first cloned and started the Dify service in an environment with internet access.
Then I downloaded the model provider plugins I wanted to use, including vllm and OpenAI-API-compatible.
After that, I copied the entire volumes folder into my target intranet (air-gapped) environment.

In the intranet environment, Dify can be started successfully, and the models can be "" in both vllm and OpenAI-API-compatible plugins. However, I still encountered some issues.

Problem Description

  1. After adding a model, the UI still shows 0 models.
    When I click "Add Model" on the right side and select the one I just added, a popup shows:
    "Can't add same credential"
    But in fact, I only added this one model.
Image
  1. This issue happens with both vllm and OpenAI-API-compatible plugins.

Related Screenshots

  1. Credential configuration details
Image
  1. Browser developer tools "switch" request details

Image

Image

Special Context

I am deploying Dify in a fully isolated intranet environment (no internet access).
Not sure if this situation may have an impact.

Looking forward to your help, thanks a lot!

✔️ Error log

plugin_daemon-1  | 2025/09/02 02:23:12 [Recovery] 2025/09/02 - 02:23:12 panic recovered:
plugin_daemon-1  | runtime error: invalid memory address or nil pointer dereference
plugin_daemon-1  | /usr/local/go/src/runtime/panic.go:262 (0x4771b8)
plugin_daemon-1  | /usr/local/go/src/runtime/signal_unix.go:917 (0x477188)
plugin_daemon-1  | [GIN] 2025/09/02 - 02:23:12 | 500 |    3.307466ms |   192.168.144.8 | POST     "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/dispatch/model/schema"
plugin_daemon-1  | /app/internal/core/plugin_manager/local_runtime/stdio.go:85 (0xdbf0b2)
plugin_daemon-1  | /app/internal/core/plugin_manager/local_runtime/io.go:16 (0xdbca1a)
plugin_daemon-1  | /app/internal/core/plugin_daemon/generic.go:27 (0xfda4d6)
plugin_daemon-1  | /app/internal/core/plugin_daemon/model.gen.go:161 (0x1008dab)
plugin_daemon-1  | /app/internal/service/model.gen.go:210 (0x1008d92)
plugin_daemon-1  | /app/internal/service/base_sse.go:116 (0x1013cfa)
plugin_daemon-1  | /app/internal/service/base_sse.go:44 (0x1013f5e)
plugin_daemon-1  | /app/internal/service/base_sse.go:114 (0x1013c44)
plugin_daemon-1  | /app/internal/service/model.gen.go:208 (0x1008d51)
plugin_daemon-1  | /app/internal/server/controllers/model.gen.go:163 (0x1ec7fa8)
plugin_daemon-1  | /app/internal/server/controllers/base.go:53 (0x103de1b)
plugin_daemon-1  | /app/internal/server/controllers/base.go:31 (0x103e281)
plugin_daemon-1  | /app/internal/server/controllers/base.go:37 (0x103dd35)
plugin_daemon-1  | /app/internal/server/controllers/model.gen.go:160 (0x1ec7ef3)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a)
plugin_daemon-1  | /app/internal/server/middleware.go:170 (0x1ecaa33)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a)
plugin_daemon-1  | /app/internal/server/middleware.go:97 (0x1ecaad3)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a)
plugin_daemon-1  | /app/internal/server/middleware.go:66 (0x1ecedb1)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a)
plugin_daemon-1  | /app/internal/server/controllers/health_check.go:28 (0x1eceaa4)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a)
plugin_daemon-1  | /app/internal/server/middleware.go:24 (0x1eca1b4)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a)
plugin_daemon-1  | /app/internal/server/controllers/health_check.go:20 (0x1ecea44)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf964ce)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/recovery.go:102 (0xf964bb)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf95604)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249 (0xf955eb)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf949f1)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633 (0xf94460)
plugin_daemon-1  | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589 (0xf93f91)
plugin_daemon-1  | /usr/local/go/src/net/http/server.go:3210 (0x7e26cd)
plugin_daemon-1  | /usr/local/go/src/net/http/server.go:2092 (0x7c1bef)
plugin_daemon-1  | /usr/local/go/src/runtime/asm_amd64.s:1700 (0x47d3a0)
plugin_daemon-1  |
plugin_daemon-1  | [GIN] 2025/09/02 - 02:23:14 | 200 |    1.814886ms |   192.168.144.8 | GET      "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256"
plugin_daemon-1  | [GIN] 2025/09/02 - 02:23:31 | 200 |    2.141974ms |   192.168.144.8 | GET      "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256"
plugin_daemon-1  | [GIN] 2025/09/02 - 02:23:31 | 200 |    1.758411ms |   192.168.144.8 | GET      "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256"
plugin_daemon-1  | [GIN] 2025/09/02 - 02:24:36 | 200 |    1.852599ms |   192.168.144.8 | GET      "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256"
nginx-1          | 22.52.18.143 - - [02/Sep/2025:02:24:36 +0000] "POST /console/api/workspaces/current/model-**providers/yangyaofei/vllm/vllm/models/credentials/switch HTTP/1.1" 400 76 "http://20.52.8.71:8081/tools" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" "-"**
Originally created by @hagemon on GitHub (Sep 1, 2025). ### Self Checks - [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general). - [x] I have searched for existing issues [Dify issues](https://github.com/langgenius/dify/issues) & [Dify Official Plugins](https://github.com/langgenius/dify-official-plugins/issues), including closed ones. - [x] I confirm that I am using English to submit this report (我已阅读并同意 [Language Policy](https://github.com/langgenius/dify/issues/1542)). - [x] [FOR CHINESE USERS] 请务必使用英文提交 Issue,否则会被关闭。谢谢!:) - [x] Please do not modify this template :) and fill in all the required fields. ### Dify version 1.8.0 ### Plugin version vllm 0.1.5, OpenAI-API-compatible 0.0.22 ### Cloud or Self Hosted Self Hosted (Docker) ### Steps to reproduce ## Background I first cloned and started the Dify service in an environment with internet access. Then I downloaded the model provider plugins I wanted to use, including **vllm** and **OpenAI-API-compatible**. After that, I copied the entire `volumes` folder into my target **intranet (air-gapped) environment**. In the intranet environment, Dify can be started successfully, and the models can be "" in both vllm and OpenAI-API-compatible plugins. However, I still encountered some issues. ## Problem Description 1. After adding a model, the UI still shows **0 models**. When I click "Add Model" on the right side and select the one I just added, a popup shows: **"Can't add same credential"** But in fact, I only added this one model. <img width="2897" height="1197" alt="Image" src="https://github.com/user-attachments/assets/4842bdd0-8c13-44f1-8213-d1e378fca8cf" /> 2. This issue happens with **both vllm and OpenAI-API-compatible** plugins. ## Related Screenshots 1. Credential configuration details <img width="771" height="973" alt="Image" src="https://github.com/user-attachments/assets/445a5ecf-e1e5-4e66-af30-048649c84244" /> 2. Browser developer tools "switch" request details ![Image](https://github.com/user-attachments/assets/6ea997bc-7a0d-4ed7-a1a4-d068d831b5d4) ![Image](https://github.com/user-attachments/assets/681fe527-2f6d-4015-8886-e5b9e7424d72) ## Special Context I am deploying Dify in a **fully isolated intranet environment (no internet access)**. Not sure if this situation may have an impact. Looking forward to your help, thanks a lot! ### ✔️ Error log ``` plugin_daemon-1 | 2025/09/02 02:23:12 [Recovery] 2025/09/02 - 02:23:12 panic recovered: plugin_daemon-1 | runtime error: invalid memory address or nil pointer dereference plugin_daemon-1 | /usr/local/go/src/runtime/panic.go:262 (0x4771b8) plugin_daemon-1 | /usr/local/go/src/runtime/signal_unix.go:917 (0x477188) plugin_daemon-1 | [GIN] 2025/09/02 - 02:23:12 | 500 | 3.307466ms | 192.168.144.8 | POST "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/dispatch/model/schema" plugin_daemon-1 | /app/internal/core/plugin_manager/local_runtime/stdio.go:85 (0xdbf0b2) plugin_daemon-1 | /app/internal/core/plugin_manager/local_runtime/io.go:16 (0xdbca1a) plugin_daemon-1 | /app/internal/core/plugin_daemon/generic.go:27 (0xfda4d6) plugin_daemon-1 | /app/internal/core/plugin_daemon/model.gen.go:161 (0x1008dab) plugin_daemon-1 | /app/internal/service/model.gen.go:210 (0x1008d92) plugin_daemon-1 | /app/internal/service/base_sse.go:116 (0x1013cfa) plugin_daemon-1 | /app/internal/service/base_sse.go:44 (0x1013f5e) plugin_daemon-1 | /app/internal/service/base_sse.go:114 (0x1013c44) plugin_daemon-1 | /app/internal/service/model.gen.go:208 (0x1008d51) plugin_daemon-1 | /app/internal/server/controllers/model.gen.go:163 (0x1ec7fa8) plugin_daemon-1 | /app/internal/server/controllers/base.go:53 (0x103de1b) plugin_daemon-1 | /app/internal/server/controllers/base.go:31 (0x103e281) plugin_daemon-1 | /app/internal/server/controllers/base.go:37 (0x103dd35) plugin_daemon-1 | /app/internal/server/controllers/model.gen.go:160 (0x1ec7ef3) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a) plugin_daemon-1 | /app/internal/server/middleware.go:170 (0x1ecaa33) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a) plugin_daemon-1 | /app/internal/server/middleware.go:97 (0x1ecaad3) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a) plugin_daemon-1 | /app/internal/server/middleware.go:66 (0x1ecedb1) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a) plugin_daemon-1 | /app/internal/server/controllers/health_check.go:28 (0x1eceaa4) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a) plugin_daemon-1 | /app/internal/server/middleware.go:24 (0x1eca1b4) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf8896a) plugin_daemon-1 | /app/internal/server/controllers/health_check.go:20 (0x1ecea44) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf964ce) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/recovery.go:102 (0xf964bb) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf95604) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249 (0xf955eb) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0xf949f1) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633 (0xf94460) plugin_daemon-1 | /go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589 (0xf93f91) plugin_daemon-1 | /usr/local/go/src/net/http/server.go:3210 (0x7e26cd) plugin_daemon-1 | /usr/local/go/src/net/http/server.go:2092 (0x7c1bef) plugin_daemon-1 | /usr/local/go/src/runtime/asm_amd64.s:1700 (0x47d3a0) plugin_daemon-1 | plugin_daemon-1 | [GIN] 2025/09/02 - 02:23:14 | 200 | 1.814886ms | 192.168.144.8 | GET "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256" plugin_daemon-1 | [GIN] 2025/09/02 - 02:23:31 | 200 | 2.141974ms | 192.168.144.8 | GET "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256" plugin_daemon-1 | [GIN] 2025/09/02 - 02:23:31 | 200 | 1.758411ms | 192.168.144.8 | GET "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256" plugin_daemon-1 | [GIN] 2025/09/02 - 02:24:36 | 200 | 1.852599ms | 192.168.144.8 | GET "/plugin/b8b5288b-5ce8-4271-8a2a-88cc78e750db/management/models?page=1&page_size=256" nginx-1 | 22.52.18.143 - - [02/Sep/2025:02:24:36 +0000] "POST /console/api/workspaces/current/model-**providers/yangyaofei/vllm/vllm/models/credentials/switch HTTP/1.1" 400 76 "http://20.52.8.71:8081/tools" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" "-"** ```
yindo added the bug label 2026-02-16 10:19:53 -05:00
yindo closed this issue 2026-02-16 10:19:53 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 1, 2025):

Hi @hagemon! I'm Dosu and I’m helping the dify-official-plugins team.

Your issue is caused by a recent change in Dify's credential management: in offline (air-gapped) environments, the new credential and model tables require a manual data migration step. When running migrations in offline mode, Dify skips automatic migration of provider credentials and models, so if you haven't manually run the migration functions, the backend may think the credential already exists (even if the UI shows none), resulting in the "Can't add same credential" error and missing models in the UI. This is confirmed by the recent PRs that introduced multi-model credential support and offline migration compatibility fixes—manual execution of migration scripts is now required in air-gapped setups to properly initialize credential and model data [PR #24795] [PR #24451].

To resolve this, manually run the migration functions (like migrate_existing_providers_data and migrate_existing_provider_models_data) in the relevant Alembic migration scripts inside your offline environment. This should sync your credential/model data and allow you to add models and credentials as expected.

The plugin daemon's panic (nil pointer dereference) is likely a separate bug triggered by missing or improperly migrated data in offline mode, but fixing the migration should resolve both symptoms.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 1, 2025): <!-- Greeting --> Hi @hagemon! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the dify-official-plugins team. <!-- Answer --> Your issue is caused by a recent change in Dify's credential management: in offline (air-gapped) environments, the new credential and model tables require a manual data migration step. When running migrations in offline mode, Dify skips automatic migration of provider credentials and models, so if you haven't manually run the migration functions, the backend may think the credential already exists (even if the UI shows none), resulting in the "Can't add same credential" error and missing models in the UI. This is confirmed by the recent PRs that introduced multi-model credential support and offline migration compatibility fixes—manual execution of migration scripts is now required in air-gapped setups to properly initialize credential and model data [[PR #24795]](https://github.com/langgenius/dify/pull/24795) [[PR #24451]](https://github.com/langgenius/dify/pull/24451). To resolve this, manually run the migration functions (like `migrate_existing_providers_data` and `migrate_existing_provider_models_data`) in the relevant Alembic migration scripts inside your offline environment. This should sync your credential/model data and allow you to add models and credentials as expected. The plugin daemon's panic (nil pointer dereference) is likely a separate bug triggered by missing or improperly migrated data in offline mode, but fixing the migration should resolve both symptoms. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/bfa2ee70-8011-4d2b-ac4b-83cc02d264c7?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@hagemon commented on GitHub (Sep 2, 2025):

@dosu

I tried some commands, for example:

docker exec -it docker-api-1 bash
uv run flask migrate-data-for-plugin

but it still didn’t work (maybe because this command is unrelated to my issue).

I also tried:

uv run flask db upgrade --sql

but it returned:

File "/app/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py", line 86, in migrate_existing_providers_data
).fetchall()
  ^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'fetchall'

In addition, I tried to look for the migrate_existing_providers_data and migrate_existing_provider_models_data functions, but I couldn’t find them. Where can I locate the Alembic migration scripts?

By the way, I’m actually using version 1.8.0, and I downloaded and copied the volumes directly in this version, so it seems there shouldn’t be any old data in my setup.

@hagemon commented on GitHub (Sep 2, 2025): @dosu I tried some commands, for example: ```bash docker exec -it docker-api-1 bash uv run flask migrate-data-for-plugin ``` but it still didn’t work (maybe because this command is unrelated to my issue). I also tried: ```bash uv run flask db upgrade --sql ``` but it returned: ``` File "/app/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py", line 86, in migrate_existing_providers_data ).fetchall() ^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'fetchall' ``` In addition, I tried to look for the `migrate_existing_providers_data` and `migrate_existing_provider_models_data` functions, but I couldn’t find them. Where can I locate the Alembic migration scripts? By the way, I’m actually using version 1.8.0, and I downloaded and copied the volumes directly in this version, so it seems there shouldn’t be any old data in my setup.
Author
Owner

@dosubot[bot] commented on GitHub (Sep 2, 2025):

The migration functions you’re looking for—migrate_existing_providers_data and migrate_existing_provider_models_data—are defined directly inside the Alembic migration scripts in your Dify install:

  • api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py
  • api/migrations/versions/2025_08_13_1605-0e154742a5fa_add_provider_model_multi_credential.py
    You can open these files to see the full function definitions and logic for migrating provider and model credential data [source].

The error you hit (AttributeError: 'NoneType' object has no attribute 'fetchall') happens because Alembic’s --sql (offline mode) disables all database IO, so op.get_bind() returns None and any DB query fails. These migration scripts require a live DB connection and must be run in an online Python context (like a Flask shell or Alembic online mode) inside your Docker container—not with --sql or Alembic offline mode [source].

To fix your credential/model issue, you need to manually run these migration functions in a live Python shell with DB access. For example, inside your API container:

docker exec -it docker-api-1 bash
uv run flask shell

Then, in the shell, import and run the migration functions from the relevant migration script files. This will sync your credential/model data and resolve the “Can't add same credential” error.

If you need more detailed steps for running these migrations manually, let me know!

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 2, 2025): <!-- Answer --> The migration functions you’re looking for—`migrate_existing_providers_data` and `migrate_existing_provider_models_data`—are defined directly inside the Alembic migration scripts in your Dify install: - `api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py` - `api/migrations/versions/2025_08_13_1605-0e154742a5fa_add_provider_model_multi_credential.py` You can open these files to see the full function definitions and logic for migrating provider and model credential data [[source]](https://github.com/langgenius/dify/blob/067b0d07c4d1c1d6d8de2a4cf67234c71ef44d97/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py). The error you hit (`AttributeError: 'NoneType' object has no attribute 'fetchall'`) happens because Alembic’s `--sql` (offline mode) disables all database IO, so `op.get_bind()` returns `None` and any DB query fails. These migration scripts require a live DB connection and must be run in an online Python context (like a Flask shell or Alembic online mode) inside your Docker container—not with `--sql` or Alembic offline mode [[source]](https://github.com/langgenius/dify/blob/067b0d07c4d1c1d6d8de2a4cf67234c71ef44d97/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py). To fix your credential/model issue, you need to manually run these migration functions in a live Python shell with DB access. For example, inside your API container: ```bash docker exec -it docker-api-1 bash uv run flask shell ``` Then, in the shell, import and run the migration functions from the relevant migration script files. This will sync your credential/model data and resolve the “Can't add same credential” error. If you need more detailed steps for running these migrations manually, let me know! <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/77333706-b8fd-458d-8d57-ce6d70662088?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@hagemon commented on GitHub (Sep 2, 2025):

@dosu

Please give me more detailed steps for running these migrations manually.

@hagemon commented on GitHub (Sep 2, 2025): @dosu Please give me more detailed steps for running these migrations manually.
Author
Owner

@dosubot[bot] commented on GitHub (Sep 2, 2025):

Here are the detailed steps to manually run the required migration functions in your Dify Docker environment:

  1. Enter your API container and start a Flask shell:

    docker exec -it docker-api-1 bash
    uv run flask shell
    
  2. In the Flask shell, use Python's importlib to load and run the migration functions:

    import importlib.util
    
    # Path to the migration scripts (adjust if your container uses a different path)
    path1 = "/app/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py"
    path2 = "/app/api/migrations/versions/2025_08_13_1605-0e154742a5fa_add_provider_model_multi_credential.py"
    
    # Load and run migrate_existing_providers_data
    spec1 = importlib.util.spec_from_file_location("cred_mig", path1)
    cred_mig = importlib.util.module_from_spec(spec1)
    spec1.loader.exec_module(cred_mig)
    cred_mig.migrate_existing_providers_data()
    
    # Load and run migrate_existing_provider_models_data
    spec2 = importlib.util.spec_from_file_location("model_mig", path2)
    model_mig = importlib.util.module_from_spec(spec2)
    spec2.loader.exec_module(model_mig)
    model_mig.migrate_existing_provider_models_data()
    
  3. Watch for errors in the shell output. If both functions run without exceptions, the migration is complete.

  4. Restart your Dify containers to ensure the UI and backend pick up the changes:

    docker compose restart
    
  5. Verify in the UI that your models and credentials now appear and you can add new ones without the "Can't add same credential" error.

These steps use the actual migration scripts and functions as defined in your Dify install, and ensure the migrations run with a live DB connection as required for offline environments. For reference, see the migration script source and PR #24795.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 2, 2025): <!-- Answer --> Here are the detailed steps to manually run the required migration functions in your Dify Docker environment: 1. **Enter your API container and start a Flask shell:** ```bash docker exec -it docker-api-1 bash uv run flask shell ``` 2. **In the Flask shell, use Python's importlib to load and run the migration functions:** ```python import importlib.util # Path to the migration scripts (adjust if your container uses a different path) path1 = "/app/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py" path2 = "/app/api/migrations/versions/2025_08_13_1605-0e154742a5fa_add_provider_model_multi_credential.py" # Load and run migrate_existing_providers_data spec1 = importlib.util.spec_from_file_location("cred_mig", path1) cred_mig = importlib.util.module_from_spec(spec1) spec1.loader.exec_module(cred_mig) cred_mig.migrate_existing_providers_data() # Load and run migrate_existing_provider_models_data spec2 = importlib.util.spec_from_file_location("model_mig", path2) model_mig = importlib.util.module_from_spec(spec2) spec2.loader.exec_module(model_mig) model_mig.migrate_existing_provider_models_data() ``` 3. **Watch for errors in the shell output.** If both functions run without exceptions, the migration is complete. 4. **Restart your Dify containers** to ensure the UI and backend pick up the changes: ```bash docker compose restart ``` 5. **Verify in the UI** that your models and credentials now appear and you can add new ones without the "Can't add same credential" error. These steps use the actual migration scripts and functions as defined in your Dify install, and ensure the migrations run with a live DB connection as required for offline environments. For reference, see the [migration script source](https://github.com/langgenius/dify/blob/067b0d07c4d1c1d6d8de2a4cf67234c71ef44d97/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py) and [PR #24795](https://github.com/langgenius/dify/pull/24795). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/3c6d9ebb-44d5-43ab-baab-6b3dce5f0cd3?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@hagemon commented on GitHub (Sep 2, 2025):

@dosu

Both function shows

NameError: Can't invoke function 'get_bind', as the proxy object has not yet been established for the Alembic 'Operations' class. Try placing this code inside a callable.
@hagemon commented on GitHub (Sep 2, 2025): @dosu Both function shows ``` NameError: Can't invoke function 'get_bind', as the proxy object has not yet been established for the Alembic 'Operations' class. Try placing this code inside a callable. ```
Author
Owner

@dosubot[bot] commented on GitHub (Sep 2, 2025):

The error happens because the migration functions use Alembic's op.get_bind(), which only works inside an Alembic migration run—not in a Flask shell or standalone Python script. To run these migrations manually in your offline Docker environment, you need to adapt the migration logic to use a direct SQLAlchemy connection from your Flask app instead of Alembic's op object.

Here's how you can do it:

  1. Start a Flask shell inside your API container:

    docker exec -it docker-api-1 bash
    uv run flask shell
    
  2. In the shell, run a script like this (adjust paths if needed):

    import sqlalchemy as sa
    from sqlalchemy.sql import table, column
    from api.models import db
    from libs.uuid_utils import uuidv7
    
    # Define table structures
    providers_table = table('providers',
        column('id', sa.String()),
        column('tenant_id', sa.String()),
        column('provider_name', sa.String()),
        column('encrypted_config', sa.Text()),
        column('created_at', sa.DateTime()),
        column('updated_at', sa.DateTime()),
        column('credential_id', sa.String()),
    )
    
    provider_credential_table = table('provider_credentials',
        column('id', sa.String()),
        column('tenant_id', sa.String()),
        column('provider_name', sa.String()),
        column('credential_name', sa.String()),
        column('encrypted_config', sa.Text()),
        column('created_at', sa.DateTime()),
        column('updated_at', sa.DateTime())
    )
    
    # Get a connection from SQLAlchemy
    conn = db.engine.connect()
    
    # Query all existing providers with encrypted_config
    existing_providers = conn.execute(
        sa.select(
            providers_table.c.id,
            providers_table.c.tenant_id,
            providers_table.c.provider_name,
            providers_table.c.encrypted_config,
            providers_table.c.created_at,
            providers_table.c.updated_at
        ).where(providers_table.c.encrypted_config.isnot(None))
    ).fetchall()
    
    for provider in existing_providers:
        credential_id = str(uuidv7())
        if not provider.encrypted_config or provider.encrypted_config.strip() == '':
            continue
    
        # Insert into provider_credentials
        conn.execute(
            provider_credential_table.insert().values(
                id=credential_id,
                tenant_id=provider.tenant_id,
                provider_name=provider.provider_name,
                credential_name='API_KEY1',
                encrypted_config=provider.encrypted_config,
                created_at=provider.created_at,
                updated_at=provider.updated_at
            )
        )
    
        # Update providers table with credential_id
        conn.execute(
            providers_table.update()
            .where(providers_table.c.id == provider.id)
            .values(credential_id=credential_id)
        )
    
    conn.close()
    

    You can adapt the same pattern for the provider_models migration by copying the logic from the Alembic script and replacing op.get_bind() with db.engine.connect().

Important: Always back up your database before running manual migration scripts. Test in a non-production environment if possible.

This approach directly uses SQLAlchemy and your Flask app's DB connection, bypassing Alembic's migration context and avoiding the get_bind error. For reference, see the migration logic in the original Alembic script.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 2, 2025): <!-- Answer --> The error happens because the migration functions use Alembic's `op.get_bind()`, which only works inside an Alembic migration run—not in a Flask shell or standalone Python script. To run these migrations manually in your offline Docker environment, you need to adapt the migration logic to use a direct SQLAlchemy connection from your Flask app instead of Alembic's `op` object. Here's how you can do it: 1. **Start a Flask shell inside your API container:** ```bash docker exec -it docker-api-1 bash uv run flask shell ``` 2. **In the shell, run a script like this (adjust paths if needed):** ```python import sqlalchemy as sa from sqlalchemy.sql import table, column from api.models import db from libs.uuid_utils import uuidv7 # Define table structures providers_table = table('providers', column('id', sa.String()), column('tenant_id', sa.String()), column('provider_name', sa.String()), column('encrypted_config', sa.Text()), column('created_at', sa.DateTime()), column('updated_at', sa.DateTime()), column('credential_id', sa.String()), ) provider_credential_table = table('provider_credentials', column('id', sa.String()), column('tenant_id', sa.String()), column('provider_name', sa.String()), column('credential_name', sa.String()), column('encrypted_config', sa.Text()), column('created_at', sa.DateTime()), column('updated_at', sa.DateTime()) ) # Get a connection from SQLAlchemy conn = db.engine.connect() # Query all existing providers with encrypted_config existing_providers = conn.execute( sa.select( providers_table.c.id, providers_table.c.tenant_id, providers_table.c.provider_name, providers_table.c.encrypted_config, providers_table.c.created_at, providers_table.c.updated_at ).where(providers_table.c.encrypted_config.isnot(None)) ).fetchall() for provider in existing_providers: credential_id = str(uuidv7()) if not provider.encrypted_config or provider.encrypted_config.strip() == '': continue # Insert into provider_credentials conn.execute( provider_credential_table.insert().values( id=credential_id, tenant_id=provider.tenant_id, provider_name=provider.provider_name, credential_name='API_KEY1', encrypted_config=provider.encrypted_config, created_at=provider.created_at, updated_at=provider.updated_at ) ) # Update providers table with credential_id conn.execute( providers_table.update() .where(providers_table.c.id == provider.id) .values(credential_id=credential_id) ) conn.close() ``` You can adapt the same pattern for the `provider_models` migration by copying the logic from the Alembic script and replacing `op.get_bind()` with `db.engine.connect()`. **Important:** Always back up your database before running manual migration scripts. Test in a non-production environment if possible. This approach directly uses SQLAlchemy and your Flask app's DB connection, bypassing Alembic's migration context and avoiding the `get_bind` error. For reference, see the migration logic in the [original Alembic script](https://github.com/langgenius/dify/blob/067b0d07c4d1c1d6d8de2a4cf67234c71ef44d97/api/migrations/versions/2025_08_09_1553-e8446f481c1e_add_provider_credential_pool_support.py). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/82811a87-4512-48ac-a35e-1ac1963ecf78?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@hagemon commented on GitHub (Sep 2, 2025):

@dosu

After executing the script, I found that table "providers" does not have column named "encrypted_config".

I checked the columns by the following code:

inspector = sa.inspect(conn)
provider_columns = inspector.get_columns("providers")
for col in provider_columns:
    print(col["name"])
credentials_columns = inspector.get_columns("provider_credentials")
for col in credentials_columns:
    print(col["name"])

while the outputs are:

>>> provider_columns = inspector.get_columns("providers")
>>> for col in provider_columns:
...     print(col["name"])
...
id
tenant_id
provider_name
provider_type
is_valid
last_used
quota_type
quota_limit
quota_used
created_at
updated_at
credential_id
>>> credentials_columns = inspector.get_columns("provider_credentials")
>>> for col in credentials_columns:
...     print(col["name"])
...
id
tenant_id
provider_name
credential_name
encrypted_config
created_at
updated_at

@hagemon commented on GitHub (Sep 2, 2025): @dosu After executing the script, I found that table "providers" does not have column named "encrypted_config". I checked the columns by the following code: ```python inspector = sa.inspect(conn) provider_columns = inspector.get_columns("providers") for col in provider_columns: print(col["name"]) credentials_columns = inspector.get_columns("provider_credentials") for col in credentials_columns: print(col["name"]) ``` while the outputs are: ``` >>> provider_columns = inspector.get_columns("providers") >>> for col in provider_columns: ... print(col["name"]) ... id tenant_id provider_name provider_type is_valid last_used quota_type quota_limit quota_used created_at updated_at credential_id >>> credentials_columns = inspector.get_columns("provider_credentials") >>> for col in credentials_columns: ... print(col["name"]) ... id tenant_id provider_name credential_name encrypted_config created_at updated_at ```
Author
Owner

@dosubot[bot] commented on GitHub (Sep 2, 2025):

The reason your script failed is because the encrypted_config column was intentionally removed from the providers table as part of Dify's recent migration to a multi-credential architecture. All provider credential data is now stored in the provider_credentials table, and the providers table just links to it via the credential_id column (details, migration PR). This matches your current schema and is expected.

Because of this, the migration scripts (and any manual scripts based on them) that reference providers.encrypted_config will not work if your database was initialized after the migration—there is no data to migrate, and the column doesn't exist.

What to do now:
You should directly manage credentials using the current schema. To add a provider credential, insert a row into provider_credentials (with your config in encrypted_config), then set the credential_id field in the relevant providers row to point to your new credential. You can do this using SQLAlchemy in a Flask shell or with direct SQL. The same pattern applies for model credentials with the provider_model_credentials and provider_models tables.

If you want a code example or step-by-step for this, let me know what provider/model you want to add and I can help you craft the exact commands.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 2, 2025): <!-- Answer --> The reason your script failed is because the `encrypted_config` column was intentionally removed from the `providers` table as part of Dify's recent migration to a multi-credential architecture. All provider credential data is now stored in the `provider_credentials` table, and the `providers` table just links to it via the `credential_id` column ([details](https://github.com/langgenius/dify/blob/7b379e2a61f2b3dda573336f03272d8e44b13af7/api/models/provider.py), [migration PR](https://github.com/langgenius/dify/pull/24451)). This matches your current schema and is expected. Because of this, the migration scripts (and any manual scripts based on them) that reference `providers.encrypted_config` will not work if your database was initialized after the migration—there is no data to migrate, and the column doesn't exist. **What to do now:** You should directly manage credentials using the current schema. To add a provider credential, insert a row into `provider_credentials` (with your config in `encrypted_config`), then set the `credential_id` field in the relevant `providers` row to point to your new credential. You can do this using SQLAlchemy in a Flask shell or with direct SQL. The same pattern applies for model credentials with the `provider_model_credentials` and `provider_models` tables. If you want a code example or step-by-step for this, let me know what provider/model you want to add and I can help you craft the exact commands. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/5953ae3a-f157-49ee-88a6-fefab8c0af19?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@hagemon commented on GitHub (Sep 2, 2025):

@dosu

give me a code example and steps to directly insert a vllm model provider into provider_credentials, you can use some example credential information, and I will replace them with my own.

@hagemon commented on GitHub (Sep 2, 2025): @dosu give me a code example and steps to directly insert a vllm model provider into provider_credentials, you can use some example credential information, and I will replace them with my own.
Author
Owner

@dosubot[bot] commented on GitHub (Sep 2, 2025):

Here’s how you can directly insert a vllm provider credential into the provider_credentials table and link it to your vllm provider using SQLAlchemy in the Flask shell:

  1. Start the Flask shell in your API container:

    docker exec -it docker-api-1 bash
    uv run flask shell
    
  2. In the shell, run the following Python code (replace the example values with your real API key, endpoint, etc.):

    import uuid
    import json
    from datetime import datetime
    from api.models import db
    
    # 1. Find your tenant_id (replace with your actual tenant_id if known)
    result = db.session.execute("SELECT id FROM tenants LIMIT 1")
    tenant_id = result.fetchone()[0]
    
    # 2. Find your vllm provider row (replace with your actual provider_name if needed)
    result = db.session.execute(
        "SELECT id FROM providers WHERE provider_name = 'vllm' AND tenant_id = %s", (tenant_id,)
    )
    provider_row = result.fetchone()
    if not provider_row:
        raise Exception("No vllm provider found for this tenant. Please create one in the UI first.")
    provider_id = provider_row[0]
    
    # 3. Prepare credential info
    credential_id = str(uuid.uuid4())
    now = datetime.utcnow()
    encrypted_config = json.dumps({
        "api_key": "sk-xxxxxx",  # <-- replace with your real API key
        "endpoint_url": "http://your-vllm-endpoint/v1"  # <-- replace with your real endpoint
    })
    
    # 4. Insert into provider_credentials
    db.session.execute(
        """
        INSERT INTO provider_credentials
            (id, tenant_id, provider_name, credential_name, encrypted_config, created_at, updated_at)
        VALUES
            (%s, %s, %s, %s, %s, %s, %s)
        """,
        (
            credential_id,
            tenant_id,
            "vllm",
            "API_KEY1",  # You can use any name you want
            encrypted_config,
            now,
            now
        )
    )
    
    # 5. Link this credential to your vllm provider
    db.session.execute(
        "UPDATE providers SET credential_id = %s WHERE id = %s",
        (credential_id, provider_id)
    )
    
    db.session.commit()
    print("Inserted vllm credential and linked it to provider.")
    
  3. Restart your Dify containers:

    docker compose restart
    
  4. Check the Dify UI: Your vllm credential should now appear and be usable.

Notes:

  • The encrypted_config field must be a JSON string with the keys expected by vllm (usually api_key and endpoint_url).
  • If you have multiple tenants or providers, adjust the queries to select the correct one.
  • If you don’t have a vllm provider row yet, create it in the UI first, then run this script.

This approach matches the current Dify schema and credential management logic (reference).

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 2, 2025): <!-- Answer --> Here’s how you can directly insert a vllm provider credential into the provider_credentials table and link it to your vllm provider using SQLAlchemy in the Flask shell: 1. **Start the Flask shell in your API container:** ```bash docker exec -it docker-api-1 bash uv run flask shell ``` 2. **In the shell, run the following Python code (replace the example values with your real API key, endpoint, etc.):** ```python import uuid import json from datetime import datetime from api.models import db # 1. Find your tenant_id (replace with your actual tenant_id if known) result = db.session.execute("SELECT id FROM tenants LIMIT 1") tenant_id = result.fetchone()[0] # 2. Find your vllm provider row (replace with your actual provider_name if needed) result = db.session.execute( "SELECT id FROM providers WHERE provider_name = 'vllm' AND tenant_id = %s", (tenant_id,) ) provider_row = result.fetchone() if not provider_row: raise Exception("No vllm provider found for this tenant. Please create one in the UI first.") provider_id = provider_row[0] # 3. Prepare credential info credential_id = str(uuid.uuid4()) now = datetime.utcnow() encrypted_config = json.dumps({ "api_key": "sk-xxxxxx", # <-- replace with your real API key "endpoint_url": "http://your-vllm-endpoint/v1" # <-- replace with your real endpoint }) # 4. Insert into provider_credentials db.session.execute( """ INSERT INTO provider_credentials (id, tenant_id, provider_name, credential_name, encrypted_config, created_at, updated_at) VALUES (%s, %s, %s, %s, %s, %s, %s) """, ( credential_id, tenant_id, "vllm", "API_KEY1", # You can use any name you want encrypted_config, now, now ) ) # 5. Link this credential to your vllm provider db.session.execute( "UPDATE providers SET credential_id = %s WHERE id = %s", (credential_id, provider_id) ) db.session.commit() print("Inserted vllm credential and linked it to provider.") ``` 3. **Restart your Dify containers:** ```bash docker compose restart ``` 4. **Check the Dify UI:** Your vllm credential should now appear and be usable. **Notes:** - The encrypted_config field must be a JSON string with the keys expected by vllm (usually api_key and endpoint_url). - If you have multiple tenants or providers, adjust the queries to select the correct one. - If you don’t have a vllm provider row yet, create it in the UI first, then run this script. This approach matches the current Dify schema and credential management logic ([reference](https://github.com/langgenius/dify/blob/7b379e2a61f2b3dda573336f03272d8e44b13af7/api/models/provider.py)). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/848545b7-e081-4d48-8135-cfd162d97c2d?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@hagemon commented on GitHub (Sep 2, 2025):

@dosu

result = db.session.execute("SELECT id FROM tenants LIMIT 1")
tenant_id = result.fetchone()[0]

returns empty, but in Settings → Model Provider I can see the providers I added (vllm and openai-api-compatible), although they both show 0 models.

Could this be because I downloaded the plugins in an external environment and then copied the volumes folder directly into my isolated intranet environment?

@hagemon commented on GitHub (Sep 2, 2025): @dosu ``` result = db.session.execute("SELECT id FROM tenants LIMIT 1") tenant_id = result.fetchone()[0] ``` returns empty, but in **Settings → Model Provider** I can see the providers I added (`vllm` and `openai-api-compatible`), although they both show **0 models**. Could this be because I downloaded the plugins in an external environment and then copied the `volumes` folder directly into my isolated intranet environment?
Author
Owner

@dosubot[bot] commented on GitHub (Sep 2, 2025):

Yes, this is very likely the cause. Copying only the volumes folder (which contains plugin files and configs) from an online environment does not migrate your database state—including tenants, provider linkage, and model records. In Dify, providers and credentials are linked to tenants in the database schema, and the backend expects tenant records for normal operation. If the tenants table is empty, providers may still appear in the UI (because their records exist), but they aren't properly linked, so you see "0 models" and can't add credentials or models as expected [source].

To fix this, you need to manually create a tenant in your database and update your providers and credentials to reference the new tenant_id. This will restore proper linkage and allow the UI and backend to function correctly. Copying plugin files alone isn't enough; the database must be migrated or reconstructed for full functionality [source].

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 2, 2025): <!-- Answer --> Yes, this is very likely the cause. Copying only the `volumes` folder (which contains plugin files and configs) from an online environment does not migrate your database state—including tenants, provider linkage, and model records. In Dify, providers and credentials are linked to tenants in the database schema, and the backend expects tenant records for normal operation. If the tenants table is empty, providers may still appear in the UI (because their records exist), but they aren't properly linked, so you see "0 models" and can't add credentials or models as expected [[source](https://github.com/langgenius/dify/blob/b14119b5318a86660041b698a312d051999dc01e/api/models/provider.py)]. To fix this, you need to manually create a tenant in your database and update your providers and credentials to reference the new tenant_id. This will restore proper linkage and allow the UI and backend to function correctly. Copying plugin files alone isn't enough; the database must be migrated or reconstructed for full functionality [[source](https://github.com/langgenius/dify/discussions/22254)]. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/50d08bd4-50fa-4114-901e-4acb1b8313c7?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify-official-plugins/issues/1629)
Author
Owner

@qiulang commented on GitHub (Sep 7, 2025):

I have the same problem and I remember 1.7.2 can work.

@qiulang commented on GitHub (Sep 7, 2025): I have the same problem and I remember 1.7.2 can work.
Author
Owner

@hagemon commented on GitHub (Sep 7, 2025):

solved by using https://github.com/junjiem/dify-plugin-repackaging

@hagemon commented on GitHub (Sep 7, 2025): solved by using https://github.com/junjiem/dify-plugin-repackaging
Author
Owner

@qiulang commented on GitHub (Sep 7, 2025):

@hagemon can you explain how do you fix it? For example, first still download vllm 0.1.5 and use that tool to repackage or something else ? I am not familiar with that tool. Thanks!

@qiulang commented on GitHub (Sep 7, 2025): @hagemon can you explain how do you fix it? For example, first still download vllm 0.1.5 and use that tool to repackage or something else ? I am not familiar with that tool. Thanks!
Author
Owner

@hagemon commented on GitHub (Sep 7, 2025):

@qiulang

In online machine

Clone dify-plugin-repackaging

git clone git@github.com:junjiem/dify-plugin-repackaging.git

Download plugin

Download yangyaofei-vllm_0.1.5.difypkg file from market.

Set up environment

mv yangyaofei-vllm_0.1.5.difypkg dify-plugin-repackaging
cd dify-plugin-repackaging
chmod +x plugin_repackaging.sh

# Set up python 3.12 virtual environment
## If you use `uv`
uv venv --python 3.12
source .venv/bin/activate
uv pip install uv # uv does not have `pip` by default

## Or if you use `conda`
conda env create -n dify-plugin python=3.12
conda activate dify-plugin

## Or just skip the step if you are already in python 3.12.x

Get offline package

# Use script to download dependencies and pack it into a new difypkg
# !!! MAKE SURE YOU HAVE `unzip` INSTALLED !!!
./plugin_repackaging.sh local ./yangyaofei-vllm_0.1.5.difypkg

Then copy yangyaofei-vllm_0.1.5-offline.difypkg to your offline machine

In offline machine

Just choose something like install from local file in your dify plugin page, all done!

@hagemon commented on GitHub (Sep 7, 2025): @qiulang ## In online machine ### Clone dify-plugin-repackaging ```bash git clone git@github.com:junjiem/dify-plugin-repackaging.git ``` ### Download plugin Download `yangyaofei-vllm_0.1.5.difypkg` file from [market](https://marketplace.dify.ai/plugins/yangyaofei/vllm). ### Set up environment ```bash mv yangyaofei-vllm_0.1.5.difypkg dify-plugin-repackaging cd dify-plugin-repackaging chmod +x plugin_repackaging.sh # Set up python 3.12 virtual environment ## If you use `uv` uv venv --python 3.12 source .venv/bin/activate uv pip install uv # uv does not have `pip` by default ## Or if you use `conda` conda env create -n dify-plugin python=3.12 conda activate dify-plugin ## Or just skip the step if you are already in python 3.12.x ``` ### Get offline package ```bash # Use script to download dependencies and pack it into a new difypkg # !!! MAKE SURE YOU HAVE `unzip` INSTALLED !!! ./plugin_repackaging.sh local ./yangyaofei-vllm_0.1.5.difypkg ``` Then copy `yangyaofei-vllm_0.1.5-offline.difypkg` to your offline machine ## In offline machine Just choose something like `install from local file` in your dify plugin page, all done!
Author
Owner

@qiulang commented on GitHub (Sep 8, 2025):

@hagemon I did all you said then finally "req_id: 75afd1ccb0 PluginDaemonBadRequestError: plugin verification has been enabled, and the plugin you want to install has a bad signature".

Can't find the place to disable the plugin verification

Image
@qiulang commented on GitHub (Sep 8, 2025): @hagemon I did all you said then finally "req_id: 75afd1ccb0 PluginDaemonBadRequestError: plugin verification has been enabled, and the plugin you want to install has a bad signature". Can't find the place to disable the plugin verification <img width="596" height="246" alt="Image" src="https://github.com/user-attachments/assets/c81907fd-64d5-40e1-82f2-df2f94f545f0" />
Author
Owner

@hagemon commented on GitHub (Sep 8, 2025):

Forgot an important step.

Edit .env file in dify/docker:

# Find and set these environment variables

FORCE_VERIFYING_SIGNATURE=false

PLUGIN_MAX_PACKAGE_SIZE=524288000

NGINX_CLIENT_MAX_BODY_SIZE=500M

Then restart dify containers.

@hagemon commented on GitHub (Sep 8, 2025): Forgot an important step. Edit `.env` file in `dify/docker`: ```bash # Find and set these environment variables FORCE_VERIFYING_SIGNATURE=false PLUGIN_MAX_PACKAGE_SIZE=524288000 NGINX_CLIENT_MAX_BODY_SIZE=500M ``` Then restart dify containers.
Author
Owner

@qiulang commented on GitHub (Sep 8, 2025):

Okay even I turn off FORCE_VERIFYING_SIGNATURE in .env and restart the docker, I still failed to add model with the error

Image
@qiulang commented on GitHub (Sep 8, 2025): Okay even I turn off `FORCE_VERIFYING_SIGNATURE ` in .env and restart the docker, I still failed to add model with the error <img width="1039" height="630" alt="Image" src="https://github.com/user-attachments/assets/d825588e-b8ac-4f68-83ad-8fdf9c8c3649" />
Author
Owner

@hagemon commented on GitHub (Sep 8, 2025):

The problem might be related to the authorization name, model name, or URL configuration.

@hagemon commented on GitHub (Sep 8, 2025): The problem might be related to the authorization name, model name, or URL configuration.
Author
Owner

@qiulang commented on GitHub (Sep 8, 2025):

@hagemon I finally find out it is because the model name has to be the same as the model id.

Now I added my model finally! Thanks!!

BTW why does dify-plugin-repackaging fix the problem ?

@qiulang commented on GitHub (Sep 8, 2025): @hagemon I finally find out it is because the model name has to be the same as the model id. Now I added my model finally! Thanks!! BTW why does dify-plugin-repackaging fix the problem ?
Author
Owner

@hagemon commented on GitHub (Sep 8, 2025):

@qiulang

You can check the plugin daemon logs and also unzip the difypkg to examine its internal structure.

During the process of uploading a difypkg downloaded from the store, Dify will attempt to download the packages listed in the requirements.txt file inside the difypkg and build a runtime environment. This step requires internet access.

However, dify-plugin-repackaging downloads the dependencies for you and places them into the wheels folder, which allows Dify to detect that they are already available and skip any additional downloads.

@hagemon commented on GitHub (Sep 8, 2025): @qiulang You can check the plugin daemon logs and also unzip the difypkg to examine its internal structure. During the process of uploading a `difypkg` downloaded from the store, Dify will attempt to download the packages listed in the `requirements.txt` file inside the `difypkg` and build a runtime environment. This step requires internet access. However, `dify-plugin-repackaging` downloads the dependencies for you and places them into the wheels folder, which allows Dify to detect that they are already available and skip any additional downloads.
Author
Owner

@qiulang commented on GitHub (Sep 8, 2025):

Thanks for the explanation but why does vllm plugin fails to work for 1.8.x in the first place ?

I remember it works for 1.6.x, not sure about 1.7.x

@qiulang commented on GitHub (Sep 8, 2025): Thanks for the explanation but why does vllm plugin fails to work for 1.8.x in the first place ? I remember it works for 1.6.x, not sure about 1.7.x
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify-official-plugins#607