Skip to content

fix(model): handle string-typed ChannelInfo scan for SQLite - #6311

Open
fux-dev wants to merge 1 commit into
QuantumNous:mainfrom
fux-dev:fix/sqlite-channel-info-scan
Open

fix(model): handle string-typed ChannelInfo scan for SQLite#6311
fux-dev wants to merge 1 commit into
QuantumNous:mainfrom
fux-dev:fix/sqlite-channel-info-scan

Conversation

@fux-dev

@fux-dev fux-dev commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

📝 Description

ChannelInfo is a JSON custom type attached to Channel (model/channel.go:54, gorm:"type:json"), read/written through driver.Valuer / sql.Scanner. The original Scan only asserted a single type, []byte:

func (c *ChannelInfo) Scan(value interface{}) error {
    bytesValue, _ := value.([]byte)
    return common.Unmarshal(bytesValue, c)
}

But the value passed to Scan is determined by the underlying driver, not by GORM:

  • MySQL (go-sql-driver) and Postgres (lib/pq) hand back []byte for JSON columns — the assertion succeeds and everything works.
  • SQLite drivers (mattn / modernc) hand back a Go string for TEXT columns — the assertion fails, bytesValue is nil, common.Unmarshal(nil, c) returns immediately, and ChannelInfo stays at its zero value. The field is silently dropped.

Trigger scenario: importing a MySQL database into a SQLite deployment. The MySQL JSON column is materialized by migration tooling as a SQLite TEXT column (SQLite has no native JSON type), still holding valid JSON text. On read, the SQLite driver returns a string, which never enters the []byte branch. As a result, all multi-key fields on the channel — IsMultiKey, MultiKeySize, MultiKeyStatusList, MultiKeyPollingIndex, etc. — come back empty, and every multi-key branch in middleware/distributor.go and controller/channel.go (key polling, disable state, recovery) stops taking effect.

Fix: add a string branch at the top of Scan and decode via the project's standard common.UnmarshalJsonStr wrapper; the []byte path is unchanged.

func (c *ChannelInfo) Scan(value interface{}) error {
    // SQLite drivers return TEXT columns as strings.
    if stringValue, ok := value.(string); ok {
        return common.UnmarshalJsonStr(stringValue, c)
    }
    bytesValue, _ := value.([]byte)
    return common.Unmarshal(bytesValue, c)
}

🚀 Type of change

  • 🐛 Bug fix

🔗 Related Issue

  • None. Upstream model/channel.go on main still has the []byte-only Scan, and there is no matching issue/PR. Happy to open one if the maintainers prefer.

✅ Checklist

  • Manually authored: I wrote and reviewed this description myself (drafted with AI assistance, then manually verified against the code).
  • Not a duplicate: Searched upstream Issues / PRs — no existing fix; upstream ChannelInfo.Scan still does not handle the string type.
  • Bug fix context: Root cause explained above; can attach an issue on request.
  • Understood impact: The string / []byte branches correspond to the SQLite vs. MySQL/Postgres driver return types per the database/sql Scanner contract; behavior on MySQL/Postgres is unchanged.
  • Scoped: A single file (model/channel.go), +5 lines, no unrelated changes.
  • Locally verified: On a SQLite deployment populated by importing from MySQL, reading channel info no longer silently loses the ChannelInfo fields.
  • Security & compliance: No credentials; uses the project's common.UnmarshalJsonStr wrapper per the JSON convention.

📸 Proof of Work

(to be attached: before/after logs from a SQLite deployment whose DB was imported from MySQL)


Note: PR authored from a fork; submitter is not a historical core contributor upstream. Description + change drafted with AI assistance and manually reviewed.

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with SQLite-like database drivers when reading channel information stored as JSON text.
    • Preserved existing handling for binary JSON data.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e553be8d-043e-4176-a20b-51636d89c5f7

📥 Commits

Reviewing files that changed from the base of the PR and between 2c29a82 and 89ce3a3.

📒 Files selected for processing (1)
  • model/channel.go

Walkthrough

ChannelInfo.Scan now supports JSON column values returned as strings by SQLite-like drivers, while preserving the existing byte-based unmarshalling behavior.

Changes

Channel JSON scanning

Layer / File(s) Summary
String JSON input support
model/channel.go
ChannelInfo.Scan uses common.UnmarshalJsonStr for string inputs and retains the existing []byte path for other inputs.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Suggested reviewers: seefs001

Poem

A bunny saw JSON hop by,
As text beneath the SQLite sky.
“Strings are welcome here,”
Said the scanner with cheer—
And byte paths still safely fly!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing ChannelInfo scanning for string-typed SQLite JSON values.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@fux-dev
fux-dev force-pushed the fix/sqlite-channel-info-scan branch from 8cee60a to 2c29a82 Compare July 19, 2026 07:00
@fux-dev

fux-dev commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

📸 Proof of Work

Reproduced locally with an in-memory SQLite DB (glebarez/sqlite, the same driver new-api uses). The test file below is not committed to this PR (kept the change to 1 file / +5 lines as scoped); it's reproduced here so reviewers can re-run it.

Test (model/channel_info_scan_test.go, untracked)

package model

import (
	"testing"

	"github.com/QuantumNous/new-api/constant"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Documents the actual driver behavior that triggers the bug: glebarez/sqlite
// returns the `channel_info` TEXT column as a Go string via database/sql.
// A []byte-only Scanner cannot decode it.
func TestChannelInfoSQLiteDriverReturnsString(t *testing.T) {
	ch := &Channel{
		Id: 7001, Type: 1, Key: "k0\nk1\nk2", Status: 1,
		Name: "multi-key channel", Models: "gpt-4", Group: "default",
		ChannelInfo: ChannelInfo{IsMultiKey: true, MultiKeySize: 3, MultiKeyMode: constant.MultiKeyModePolling},
	}
	require.NoError(t, DB.Create(ch).Error)

	var raw string
	require.NoError(t, DB.Raw("SELECT channel_info FROM channels WHERE id = ?", ch.Id).Row().Scan(&raw))
	require.NotEmpty(t, raw)
	t.Logf("driver returned channel_info as string (len=%d): %s", len(raw), raw)

	var info ChannelInfo
	require.NoError(t, info.Scan(raw))
	assert.True(t, info.IsMultiKey)
	assert.Equal(t, 3, info.MultiKeySize)
	assert.Equal(t, constant.MultiKeyModePolling, info.MultiKeyMode)
}

func TestChannelInfoScan_HandlesStringDirectly(t *testing.T) {
	payload := `{"is_multi_key":true,"multi_key_size":2,"multi_key_status_list":{"0":1,"1":1},"multi_key_polling_index":0,"multi_key_mode":"polling"}`
	var got ChannelInfo
	require.NoError(t, got.Scan(payload))
	assert.True(t, got.IsMultiKey)
	assert.Equal(t, 2, got.MultiKeySize)
	assert.Equal(t, map[int]int{0: 1, 1: 1}, got.MultiKeyStatusList)
	assert.Equal(t, constant.MultiKeyModePolling, got.MultiKeyMode)
}

func TestChannelInfoScan_StillHandlesBytes(t *testing.T) {
	payload := []byte(`{"is_multi_key":true,"multi_key_size":1,"multi_key_polling_index":0,"multi_key_mode":"polling"}`)
	var got ChannelInfo
	require.NoError(t, got.Scan(payload))
	assert.True(t, got.IsMultiKey)
	assert.Equal(t, 1, got.MultiKeySize)
}

Result — before this PR (reverted Scan to the upstream []byte-only version)

=== RUN   TestChannelInfoScan_HandlesStringDirectly
    Error: unexpected end of JSON input
--- FAIL: TestChannelInfoScan_HandlesStringDirectly
=== RUN   TestChannelInfoScan_StillHandlesBytes
--- PASS: TestChannelInfoScan_StillHandlesBytes
=== RUN   TestChannelInfoSQLiteDriverReturnsString
    channel_info_scan_test.go:61: driver returned channel_info as string (len=124): {"is_multi_key":true,"multi_key_size":3,"multi_key_status_list":null,"multi_key_polling_index":0,"multi_key_mode":"polling"}
    Error: unexpected end of JSON input
--- FAIL: TestChannelInfoSQLiteDriverReturnsString
FAIL

Observations:

  • glebarez/sqlite returns the TEXT column as a Go string (visible in the log line) — confirmed.
  • The string-typed value falls through the []byte assertion; bytesValue is nil; Unmarshal(nil, ...) returns "unexpected end of JSON input" and the field is silently dropped.
  • The []byte path (MySQL/Postgres) keeps working.

Result — after this PR

=== RUN   TestChannelInfoScan_HandlesStringDirectly
--- PASS: TestChannelInfoScan_HandlesStringDirectly
=== RUN   TestChannelInfoScan_StillHandlesBytes
--- PASS: TestChannelInfoScan_StillHandlesBytes
=== RUN   TestChannelInfoSQLiteDriverReturnsString
    channel_info_scan_test.go:61: driver returned channel_info as string (len=124): {"is_multi_key":true,"multi_key_size":3,"multi_key_status_list":null,"multi_key_polling_index":0,"multi_key_mode":"polling"}
--- PASS: TestChannelInfoSQLiteDriverReturnsString
PASS
ok  	github.com/QuantumNous/new-api/model	0.472s

Both the string path (SQLite) and the []byte path (MySQL/Postgres) decode correctly.

@fux-dev
fux-dev force-pushed the fix/sqlite-channel-info-scan branch from 2c29a82 to 89ce3a3 Compare July 19, 2026 07:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx (1)

56-57: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear the confirmation state when the dialog closes.

If the dialog is closed via an overlay click or the Escape key, the underlying Dialog component calls onOpenChange directly, bypassing handleCancel. This leaves the high-risk checkboxes and confirmation text intact the next time the dialog is opened, which defeats the purpose of the safety checks.

Add an effect to clear the state whenever the dialog is closed:

♻️ Proposed fix
   const [checkedItems, setCheckedItems] = useState<Set<number>>(new Set())
   const [confirmText, setConfirmText] = useState('')
+
+  useEffect(() => {
+    if (!open) {
+      setCheckedItems(new Set())
+      setConfirmText('')
+    }
+  }, [open])

(Don't forget to add useEffect to your React imports on line 20).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx`
around lines 56 - 57, Update the status-code risk dialog’s state management to
import and use useEffect, clearing checkedItems and confirmText whenever the
dialog’s open state becomes false. Keep handleCancel behavior intact while
ensuring overlay and Escape closures also reset the confirmation state before
the next opening.
🧹 Nitpick comments (1)
web/default/src/features/channels/constants.ts (1)

379-381: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate MODEL_FETCHABLE_TYPES literal across files.

The exact same Set literal is also defined in web/default/src/features/channels/lib/channel-form.ts (lines 379-381). Both copies were kept in sync in this PR, but duplicating this list risks future drift — one file could add a new fetchable type without the other, desyncing UI gating from settings-building logic.

♻️ Suggested consolidation
-export const MODEL_FETCHABLE_TYPES = new Set([
-  1, 4, 14, 17, 20, 23, 24, 25, 26, 27, 31, 34, 35, 40, 42, 43, 47, 48, 57, 58,
-])
+export { MODEL_FETCHABLE_TYPES } from './lib/channel-form'

(or the inverse — keep one canonical definition and import it from the other file.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/channels/constants.ts` around lines 379 - 381,
Consolidate the duplicate MODEL_FETCHABLE_TYPES definitions by keeping one
canonical Set and importing/reusing it from the other file. Update both the
constants module and channel-form logic so UI gating and settings-building
reference the same exported symbol, preserving the existing fetchable type
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/channel_upstream_update.go`:
- Around line 307-333: Update getFetchModelsResponseBody to enforce a finite
deadline for the outbound request, preferably by creating a context with timeout
and attaching it to request before client.Do. Ensure the timeout is applied even
when service.NewProxyHttpClient receives an unset RELAY_TIMEOUT, and release the
context cancellation after the request completes.

---

Outside diff comments:
In
`@web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx`:
- Around line 56-57: Update the status-code risk dialog’s state management to
import and use useEffect, clearing checkedItems and confirmText whenever the
dialog’s open state becomes false. Keep handleCancel behavior intact while
ensuring overlay and Escape closures also reset the confirmation state before
the next opening.

---

Nitpick comments:
In `@web/default/src/features/channels/constants.ts`:
- Around line 379-381: Consolidate the duplicate MODEL_FETCHABLE_TYPES
definitions by keeping one canonical Set and importing/reusing it from the other
file. Update both the constants module and channel-form logic so UI gating and
settings-building reference the same exported symbol, preserving the existing
fetchable type values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6cdcbc7-f37e-4ded-964a-f4173c192b68

📥 Commits

Reviewing files that changed from the base of the PR and between 8cee60a and 2c29a82.

📒 Files selected for processing (49)
  • controller/channel.go
  • controller/channel_upstream_update.go
  • controller/channel_upstream_update_test.go
  • controller/telegram.go
  • controller/telegram_test.go
  • controller/user.go
  • docs/openapi/api.json
  • dto/channel_settings.go
  • dto/channel_settings_test.go
  • model/channel.go
  • model/channel_settings_test.go
  • model/pricing_default.go
  • model/task_cas_test.go
  • model/token.go
  • model/twofa.go
  • model/user.go
  • model/user_authentication_test.go
  • model/user_pagination_test.go
  • relay/channel/advancedcustom/adaptor.go
  • relay/channel/advancedcustom/adaptor_test.go
  • relay/channel/codex/constants.go
  • service/codex_channel_models.go
  • service/codex_models.go
  • service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go
  • service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go
  • setting/ratio_setting/compact_suffix.go
  • web/default/src/components/data-table/core/data-table-row.tsx
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/features/channels/api.ts
  • web/default/src/features/channels/components/dialogs/advanced-custom-editor-dialog.tsx
  • web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx
  • web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
  • web/default/src/features/channels/constants.ts
  • web/default/src/features/channels/lib/advanced-custom.ts
  • web/default/src/features/channels/lib/channel-form.ts
  • web/default/src/features/models/components/models-table.tsx
  • web/default/src/features/users/api.ts
  • web/default/src/features/users/components/user-quota-cell.tsx
  • web/default/src/features/users/components/users-columns.tsx
  • web/default/src/features/users/components/users-table.tsx
  • web/default/src/features/users/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh-TW.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/static-keys.ts
💤 Files with no reviewable changes (1)
  • web/default/src/features/models/components/models-table.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx (1)

56-57: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear the confirmation state when the dialog closes.

If the dialog is closed via an overlay click or the Escape key, the underlying Dialog component calls onOpenChange directly, bypassing handleCancel. This leaves the high-risk checkboxes and confirmation text intact the next time the dialog is opened, which defeats the purpose of the safety checks.

Add an effect to clear the state whenever the dialog is closed:

♻️ Proposed fix
   const [checkedItems, setCheckedItems] = useState<Set<number>>(new Set())
   const [confirmText, setConfirmText] = useState('')
+
+  useEffect(() => {
+    if (!open) {
+      setCheckedItems(new Set())
+      setConfirmText('')
+    }
+  }, [open])

(Don't forget to add useEffect to your React imports on line 20).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx`
around lines 56 - 57, Update the status-code risk dialog’s state management to
import and use useEffect, clearing checkedItems and confirmText whenever the
dialog’s open state becomes false. Keep handleCancel behavior intact while
ensuring overlay and Escape closures also reset the confirmation state before
the next opening.
🧹 Nitpick comments (1)
web/default/src/features/channels/constants.ts (1)

379-381: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate MODEL_FETCHABLE_TYPES literal across files.

The exact same Set literal is also defined in web/default/src/features/channels/lib/channel-form.ts (lines 379-381). Both copies were kept in sync in this PR, but duplicating this list risks future drift — one file could add a new fetchable type without the other, desyncing UI gating from settings-building logic.

♻️ Suggested consolidation
-export const MODEL_FETCHABLE_TYPES = new Set([
-  1, 4, 14, 17, 20, 23, 24, 25, 26, 27, 31, 34, 35, 40, 42, 43, 47, 48, 57, 58,
-])
+export { MODEL_FETCHABLE_TYPES } from './lib/channel-form'

(or the inverse — keep one canonical definition and import it from the other file.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/channels/constants.ts` around lines 379 - 381,
Consolidate the duplicate MODEL_FETCHABLE_TYPES definitions by keeping one
canonical Set and importing/reusing it from the other file. Update both the
constants module and channel-form logic so UI gating and settings-building
reference the same exported symbol, preserving the existing fetchable type
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/channel_upstream_update.go`:
- Around line 307-333: Update getFetchModelsResponseBody to enforce a finite
deadline for the outbound request, preferably by creating a context with timeout
and attaching it to request before client.Do. Ensure the timeout is applied even
when service.NewProxyHttpClient receives an unset RELAY_TIMEOUT, and release the
context cancellation after the request completes.

---

Outside diff comments:
In
`@web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx`:
- Around line 56-57: Update the status-code risk dialog’s state management to
import and use useEffect, clearing checkedItems and confirmText whenever the
dialog’s open state becomes false. Keep handleCancel behavior intact while
ensuring overlay and Escape closures also reset the confirmation state before
the next opening.

---

Nitpick comments:
In `@web/default/src/features/channels/constants.ts`:
- Around line 379-381: Consolidate the duplicate MODEL_FETCHABLE_TYPES
definitions by keeping one canonical Set and importing/reusing it from the other
file. Update both the constants module and channel-form logic so UI gating and
settings-building reference the same exported symbol, preserving the existing
fetchable type values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6cdcbc7-f37e-4ded-964a-f4173c192b68

📥 Commits

Reviewing files that changed from the base of the PR and between 8cee60a and 2c29a82.

📒 Files selected for processing (49)
  • controller/channel.go
  • controller/channel_upstream_update.go
  • controller/channel_upstream_update_test.go
  • controller/telegram.go
  • controller/telegram_test.go
  • controller/user.go
  • docs/openapi/api.json
  • dto/channel_settings.go
  • dto/channel_settings_test.go
  • model/channel.go
  • model/channel_settings_test.go
  • model/pricing_default.go
  • model/task_cas_test.go
  • model/token.go
  • model/twofa.go
  • model/user.go
  • model/user_authentication_test.go
  • model/user_pagination_test.go
  • relay/channel/advancedcustom/adaptor.go
  • relay/channel/advancedcustom/adaptor_test.go
  • relay/channel/codex/constants.go
  • service/codex_channel_models.go
  • service/codex_models.go
  • service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go
  • service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go
  • setting/ratio_setting/compact_suffix.go
  • web/default/src/components/data-table/core/data-table-row.tsx
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/features/channels/api.ts
  • web/default/src/features/channels/components/dialogs/advanced-custom-editor-dialog.tsx
  • web/default/src/features/channels/components/dialogs/status-code-risk-dialog.tsx
  • web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
  • web/default/src/features/channels/constants.ts
  • web/default/src/features/channels/lib/advanced-custom.ts
  • web/default/src/features/channels/lib/channel-form.ts
  • web/default/src/features/models/components/models-table.tsx
  • web/default/src/features/users/api.ts
  • web/default/src/features/users/components/user-quota-cell.tsx
  • web/default/src/features/users/components/users-columns.tsx
  • web/default/src/features/users/components/users-table.tsx
  • web/default/src/features/users/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh-TW.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/static-keys.ts
💤 Files with no reviewable changes (1)
  • web/default/src/features/models/components/models-table.tsx
🛑 Comments failed to post (1)
controller/channel_upstream_update.go (1)

307-333: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether NewProxyHttpClient sets an http.Client Timeout.
rg -n -A 25 'func NewProxyHttpClient' --type=go

Repository: QuantumNous/new-api

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the proxy HTTP client implementation and related timeout settings.
git ls-files | rg '(^|/)(service|controller)/.*\.(go|ts|js)$|^service/.*\.go$|^controller/channel_upstream_update\.go$|codex_channel_models\.go$'
echo '---'
rg -n 'NewProxyHttpClient|http\.Client|Timeout|WithTimeout|NewRequestWithContext' service controller --type=go

Repository: QuantumNous/new-api

Length of output: 18211


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the proxy client implementation and the RelayTimeout default/configuration.
sed -n '120,220p' service/http_client.go
echo '---'
rg -n 'RelayTimeout' --type=go --type=toml --type=yaml --type=yml --type=json --type=ini .

Repository: QuantumNous/new-api

Length of output: 2760


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether the fetch-models path already has a timeout elsewhere in the call chain.
sed -n '300,470p' controller/channel_upstream_update.go
echo '---'
sed -n '1,120p' service/codex_channel_models.go

Repository: QuantumNous/new-api

Length of output: 8057


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the default value and configuration source for common.RelayTimeout.
rg -n 'RelayTimeout' --type=go common setting config constant .
echo '---'
sed -n '1,120p' service/http_client.go

Repository: QuantumNous/new-api

Length of output: 1310


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect where common.RelayTimeout is defined/initialized.
rg -n 'RelayTimeout\s*[:=]|type .*RelayTimeout|var .*RelayTimeout|const .*RelayTimeout' --type=go .
echo '---'
rg -n 'RelayTimeout' common setting constant --type=go

Repository: QuantumNous/new-api

Length of output: 492


Add a deadline to this shared fetch helper
service.NewProxyHttpClient only applies client.Timeout when RELAY_TIMEOUT is set; the default is 0, so this /api/channel/fetch_models path can still block indefinitely on a slow upstream. Add a per-request context timeout here or require a nonzero proxy timeout.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/channel_upstream_update.go` around lines 307 - 333, Update
getFetchModelsResponseBody to enforce a finite deadline for the outbound
request, preferably by creating a context with timeout and attaching it to
request before client.Do. Ensure the timeout is applied even when
service.NewProxyHttpClient receives an unset RELAY_TIMEOUT, and release the
context cancellation after the request completes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant