fix(api): tolerate legacy service_api end-user type on load#38271
Conversation
v1.15.0 renamed the `EndUserType` value from `service_api` to `service-api` and shipped `4f7b2c8d9a10_normalize_legacy_end_user_type` to rewrite existing rows. If a row still holds the legacy underscore value (migration not yet run, or a row created by an old instance during the upgrade window), `EnumText` deserializes it via `EndUserType(value)`, which raised `'service_api' is not a valid EndUserType` and returned HTTP 400 on every request for that end user. Add a `_missing_` handler that maps the legacy `service_api` value to `SERVICE_API`, mirroring `CreatorUserRole._missing_`, so unmigrated end users keep loading gracefully. Only the exact legacy string is tolerated; all other unknown values still raise. Fixes langgenius#38201 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JBVXLzDtt5GcRQeJfStmUw
Pyrefly Type Coverage
|
|
Generally speaking, you should run database migration before upgrading Dify application. |
When performing rolling update, the legacy enum values held by old replicas could still be committed after the migration. |
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 41fb484)
Agreed, this is a valid rolling-update concern. AFAIK, most data migrations are not currently designed or implemented with full rolling-upgrade compatibility in mind. Ideally, the safer release sequence should be:
That should address the rolling-update case. There is still one remaining edge case: if a Dify instance upgrades across multiple versions at once, it may skip the intermediate compatibility window. For that case, I think we should either require users to upgrade version by version, or recommend stopping traffic while performing a multi-version upgrade. |
Summary
Fixes #38201.
v1.15.0 renamed the
EndUserTypeenum value fromservice_api(underscore) toservice-api(hyphen) and shipped the4f7b2c8d9a10_normalize_legacy_end_user_typemigration to rewrite existing rows.However, if an
end_usersrow still holds the legacyservice_apivalue — the migration hasn't been run yet, or a row was created by an old instance during the upgrade window — the column'sEnumTexttype deserializes it viaEndUserType(value):That raises
'service_api' is not a valid EndUserType, which surfaces as HTTP 400 on every request for that end user (e.g.POST /v1/chat-messages). The reporter had to hand-fix it with SQL.Fix
Add a
_missing_handler toEndUserTypethat maps the legacyservice_apivalue toSERVICE_API, exactly mirroring the existingCreatorUserRole._missing_(which tolerates the legacyend-uservalue) in the same file. Unmigrated end users then load gracefully instead of hard-failing.service_apiis tolerated; all other unknown values still raise.service-api.Tests
Added to
api/tests/unit_tests/models/test_end_user_type.py:service_apiand canonicalservice-apiboth resolve toEndUserType.SERVICE_APIValueErrorEnumText.process_result_value("service_api", ...)(the actual column load path that failed) returnsEndUserType.SERVICE_APIuv run python -m pytest tests/unit_tests/models/test_end_user_type.py→ 9 passed.ruffandpyreflyclean.Checklist
ruff(lint) andpyrefly(type-check) on the changed files plus the focused unit tests — all pass.From Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01JBVXLzDtt5GcRQeJfStmUw