Wei-Shaw/sub2api · error

缺少 accessToken/access_token

Error message

缺少 accessToken/access_token

What it means

Returned at backend/internal/handler/admin/account_codex_import.go:625 for a non-agent-identity Codex import entry whose AccessToken is empty after all supported format extractions ran (the switch above already rejected unsupported formats with '第 N 条格式不支持'). OAuth-type Codex accounts must carry an access token; agent-identity entries are exempted earlier (item.IsAgentIdentity returns before this check).

Source

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

				return nil, fmt.Errorf("access_token 已过期: %s", tokenExpiresAt.Format(time.RFC3339))
			}
			item.TokenExpiresAt = &tokenExpiresAt
			item.Credentials["expires_at"] = tokenExpiresAt.Format(time.RFC3339)
		}
		copyCodexExtraString(raw, item.Extra, "user_image", []string{"user", "image"})
		copyCodexExtraString(raw, item.Extra, "user_picture", []string{"user", "picture"})
		copyCodexExtraString(raw, item.Extra, "account_structure", []string{"account", "structure"})
		copyCodexExtraString(raw, item.Extra, "account_residency_region", []string{"account", "residencyRegion"})
		copyCodexExtraString(raw, item.Extra, "compute_residency", []string{"account", "computeResidency"})
	default:
		return nil, fmt.Errorf("第 %d 条格式不支持", entry.Index)
	}

	if item.IsAgentIdentity {
		return item, nil
	}
	if item.AccessToken == "" {
		return nil, errors.New("缺少 accessToken/access_token")
	}
	item.Credentials["access_token"] = item.AccessToken
	if item.RefreshToken != "" {
		item.Credentials["refresh_token"] = item.RefreshToken
		item.Credentials["client_id"] = openai.ClientID
	}
	if item.IDToken != "" {
		item.Credentials["id_token"] = item.IDToken
		_ = enrichCodexImportAccountFromJWT(item, item.IDToken, false, now)
	}
	if err := enrichCodexImportAccountFromJWT(item, item.AccessToken, true, now); err != nil {
		return nil, err
	}
	if _, ok := item.Credentials["expires_at"]; !ok {
		item.WarningTexts = append(item.WarningTexts, "无法从 accessToken 解析过期时间,导入后需自行确认令牌有效性")
	}
	if item.RefreshToken == "" {
		item.WarningTexts = append(item.WarningTexts, "未包含 refresh_token,accessToken 过期后无法自动续期")

View on GitHub (pinned to 073e92d171)

Solutions

  1. Include a non-empty accessToken (or access_token) in the entry and re-import.
  2. If the token lives under a different key in your export, rename it to accessToken/access_token first.
  3. If you only have an agent identity (no OAuth token), import it as an agent-identity entry so the check is skipped.
  4. Re-run 'codex login' / re-export to regenerate a complete auth.json.

Example fix

// before
{ "refresh_token": "rt_...", "id_token": "eyJ..." }

// after
{ "accessToken": "eyJ...", "refresh_token": "rt_...", "id_token": "eyJ..." }
Defensive patterns

Strategy: validation

Validate before calling

function hasAccessToken(entry: Record<string, unknown>): boolean {
  const t = entry.accessToken ?? entry.access_token
  return typeof t === 'string' && t.trim() !== ''
}

Prevention

When it happens

Trigger: Importing a Codex auth.json that has a refresh_token or id_token but no accessToken/access_token field (or it is null/empty); an entry format that matched but maps the token under an unrecognized key.

Common situations: Exporting from a Codex CLI version that stores the token under a different key; hand-trimming the access token for privacy before import; a partially-written auth.json because the source process was interrupted mid-refresh.

Related errors


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