Wei-Shaw/sub2api · error

导入项为空

Error message

导入项为空

What it means

Returned by resolveCodexImportExpiry (backend/internal/handler/admin/account_codex_import.go:776) when called with a nil *codexImportAccount. It is a defensive precondition: every import item must be materialized before expiry resolution. Agent-identity items legitimately return all-nil results (no OAuth lifetime), but a nil item is always a bug in the caller loop, not a user input problem.

Source

Thrown at backend/internal/handler/admin/account_codex_import.go:776

}

func buildCodexCreateAccountName(base string, item *codexImportAccount, index, total int) string {
	base = strings.TrimSpace(base)
	if base == "" {
		if item == nil {
			return fmt.Sprintf("Codex 导入账号 %d", index)
		}
		return item.Name
	}
	if total > 1 {
		return fmt.Sprintf("%s #%d", base, index)
	}
	return base
}

func resolveCodexImportExpiry(req CodexSessionImportRequest, item *codexImportAccount) (*int64, *time.Time, *bool, []string, error) {
	if item == nil {
		return nil, nil, nil, nil, errors.New("导入项为空")
	}
	// Agent Identity has no OAuth access-token lifetime. Its runtime/task
	// lifecycle is handled by the upstream task recovery path, so it must not
	// be rejected or auto-paused by the OAuth import expiry policy.
	if item.IsAgentIdentity {
		return nil, nil, nil, nil, nil
	}

	var requestExpiresAt *time.Time
	if req.ExpiresAt != nil && *req.ExpiresAt > 0 {
		t := time.Unix(*req.ExpiresAt, 0).UTC()
		requestExpiresAt = &t
	}

	var accountExpiresAt *time.Time
	var credentialExpiresAt *time.Time
	warnings := make([]string, 0, 2)
	if item.RefreshToken == "" {

View on GitHub (pinned to 073e92d171)

Solutions

  1. If you are modifying the import handler, ensure items are appended only when non-nil and that error paths return before expiry resolution.
  2. Audit the caller loop for indexes into a slice that can contain nils (e.g. preallocated make([]*codexImportAccount, n) never fully filled).
  3. As an operator this error indicates a backend bug — report it with the payload shape rather than retrying the same input.

Example fix

// before
accounts := make([]*codexImportAccount, len(raw))
for i, r := range raw {
  if err := fill(r, accounts[i]); err != nil { continue } // leaves nils
  resolveCodexImportExpiry(req, accounts[i])
}

// after
var accounts []*codexImportAccount
for _, r := range raw {
  item, err := buildItem(r)
  if err != nil { return err }
  accounts = append(accounts, item)
  resolveCodexImportExpiry(req, item)
}
Defensive patterns

Strategy: validation

Validate before calling

// before calling resolveCodexImportExpiry:
if item == nil {
    return errors.New("internal: import item not constructed")
}
exp, credExp, autoPause, warns, err := resolveCodexImportExpiry(req, item)

Try / catch

if item == nil {
    if _, _, _, _, err := resolveCodexImportExpiry(req, item); err != nil {
        // defensive tripwire: log with stack, this is a code bug not user input
        log.Printf("BUG: resolveCodexImportExpiry called with nil item: %+v", err)
    }
}

Prevention

When it happens

Trigger: An import loop passing a nil slice element or a failed/never-constructed codexImportAccount into resolveCodexImportExpiry; a refactor that made the item constructor return nil on a path that was not checked.

Common situations: Code changes in the import handler that skip item construction on parse errors but still call expiry resolution; a nil pointer after an earlier error return was removed; concurrent mutation of the items slice.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/1600c1eb922e6fc2. Report an issue: GitHub.