The "Show more" button in the Codex Desktop App repeatedly toggles for long messages, causing the conversation scrollbar to continuously jump.

Resolved 💬 7 comments Opened Jul 10, 2026 by shiyo6926 Closed Jul 13, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

ChatGPT Powered by Codex & OWL 版本 26.707.31428 发布于 2026年7月10日(Due to the merger of Codex and ChatGPT today, only the ChatGPT version can be viewed.)

What subscription do you have?

gpt plus

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64

What issue are you seeing?

Phenomenon:

  • The problem is triggered by a user input message. This user input is automatically displayed by Codex as "Expand More", and it repeatedly expands and collapses when scrolling near this message, causing the scroll bar of the entire task conversation to continuously jump.
  • Switching to other tasks and then returning to this one, it is temporarily normal before rolling to the vicinity of this message.
  • Once scrolling to the vicinity of this extremely long reply, the jumping immediately begins.
  • Restarting Codex after this problem still exists because reloading the same task history will trigger it again.
  • This is not caused by the project web page or code, but a rendering issue of the conversation UI in the Codex Desktop App.

Reproduction steps:

  1. Open a project in the Codex Desktop App that already contains a long historical conversation, then input a conversation that may contain a special format / long Markdown / long code block.
  2. Scroll near the input of this conversation.
  3. The conversation (user input) shows an "Expand More" button, and "Expand More" repeatedly expands and collapses.
  4. Each height change triggers the recalculation of the scroll anchor, causing the scroll bar to continuously jump, making the entire conversation page unstable and constantly jumping.

Attempts:

  • Returning to the problem after switching tasks still exists
  • Completely closing and reopening Codex still exists
  • Attempting to open a new conversation input and copy the original conversation content, unable to reproduce, and no "Expand More" option appeared
  • In the current conversation with the bug, attempts were also made to input data with bugs, but the issue could not be reproduced either.
  • Let Codex output first. Before the input with the bug occurs, the content it replies with will be used. Then, when the user inputs the input with the bug, it cannot be reproduced.

Guess:
This conversation was previously discussing writing a prompt with Codex. The replies before and after this conversation all contain long Markdown, and multiple "-" characters caused this user input to have the "Expand More" button and be incorrectly repeatedly expanded and collapsed.

Additionally, I have reviewed the similar issues that were previously raised and none of them have been resolved but were directly closed. Please pay attention to this problem as it has occurred multiple times.

What steps can reproduce the bug?

Reproduction steps:

  1. Open a project in the Codex Desktop App that already contains a long historical conversation, then input a conversation that may contain a special format / long Markdown / long code block.
  2. Scroll near the input of this conversation.
  3. The conversation (user input) shows an "Expand More" button, and "Expand More" repeatedly expands and collapses.
  4. Each height change triggers the recalculation of the scroll anchor, causing the scroll bar to continuously jump, making the entire conversation page unstable and constantly jumping.

What is the expected behavior?

_No response_

Additional information

Before the user's input, the response content from Codex:
把下面整段直接发给其他同事的 AI 即可:

你正在给一个已有的 Agent 业务后端接入统一登录态。

先阅读当前项目结构、启动方式和依赖清单,再修改代码。不要执行 npm install、pip install 或其他安装命令;如需依赖,只修改依赖声明文件,并说明需要安装什么。

系统统一认证架构:

- 前端登录后,统一 Auth Server 将登录态写入 Redis。
- 浏览器只持有 HttpOnly Cookie:session_id。
- Redis 是唯一登录态来源。
- Redis 键格式:session:{session_id}
- Redis 值是 JSON,整体结构大致为:

{
  "schemaVersion": 2,
  "user": {
    "userId": "用户唯一标识",
    "userLoginName": "登录名",
    "userName": "姓名",
    "email": "邮箱",
    "ip": "IP",
    "domainId": "域ID",
    "orgLevel3Name": "用户单位第三级名称",
    "iat": 0,
    "exp": 0,
    "crossToken": null,
    "authMode": "SSO"
  },
  "access": {
    "isSuperAdmin": false,
    "globalRoles": [
      {
        "roleId": "角色ID",
        "roleName": "角色名称",
        "roleCode": "角色编码"
      }
    ],
    "globalMenus": [
      {
        "menuId": "菜单ID",
        "menuName": "菜单名称",
        "permissionCode": "权限编码",
        "routePath": "路由",
        "parentId": null,
        "visible": true,
        "sortOrder": 0
      }
    ]
  }
}

目标:

为当前 Agent 后端的所有业务接口增加统一认证入口:

1. 从请求 Cookie 读取 session_id。
2. 使用 Redis 查询 session:{session_id}。
3. 若 Redis Session 存在且 JSON 合法:
   - 获取 userInfo,即 session.user。
   - 同时可获取 session.access 中的角色和菜单信息。
   - 将用户上下文注入当前请求生命周期。
   - 后续业务代码可直接获取当前用户,不再从请求参数、Header 或前端传 userId。
4. 若 Cookie 不存在、Session 不存在、Session 已过期、JSON 非法:
   - 返回 HTTP 401 Unauthorized。
5. Redis 连接失败:
   - 返回 HTTP 503 Service Unavailable。
   - 不要把 Redis 故障伪装成 401。
6. 健康检查接口 `/health` 可不鉴权;其余业务 API 默认全部鉴权。

严格禁止:

- 不处理 sso_code。
- 不调用 SSO 服务。
- 不解析、验证或存储 sso_token/JWT。
- 不新增本地 token 体系。
- 不使用 localStorage token。
- 不自行创建、续期、删除 Redis Session。
- 不修改 session:{session_id} 的内容或 TTL。
- 不信任前端通过 Header、Body、Query 传来的 userId、角色或权限。
- 不把 Redis 密码、完整 session_id、用户敏感信息写入日志。

配置要求:

为当前服务新增环境变量,名称可沿用以下标准:

REDIS_URL=redis://:你的Redis密码@Redis宿主机IP:Redis映射端口/0
AUTH_SESSION_COOKIE_NAME=session_id
AUTH_SESSION_KEY_PREFIX=session:

说明:

- REDIS_URL 必须与统一 Auth Server 使用的是同一个 Redis 实例、同一个 DB 编号。
- 如果当前 Agent 后端运行在 Docker 容器中,不能把 127.0.0.1 当 Redis 地址。
- 当前部署若采用“宿主机 IP + Redis 映射端口”方式,就填写宿主机实际 IP。
- 若未来改成 Docker 私有网络,REDIS_URL 再改为 Redis 容器服务名即可,例如 redis://:密码@redis-container:6379/0。
- 不把真实密码写入代码、Dockerfile 或提交到仓库。
- 在项目现有的生产环境文件中配置,例如 agent_xxx.prod.env;不要强制使用 .env 文件名。

实现要求:

- 将认证实现为当前项目框架推荐的全局中间件、路由守卫、依赖注入或拦截器。
- FastAPI 推荐使用 Depends 或 router 级 dependencies。
- Node.js / TypeScript 推荐使用 Express / NestJS / Koa 对应的全局中间件或 Guard。
- 不要要求每个业务接口重复手写 Redis 查询。
- 请求上下文至少统一提供:
  - userId
  - userLoginName
  - userName
  - email
  - domainId
  - orgLevel3Name
  - roles
  - isSuperAdmin
  - menus
- 业务接口通过“当前用户上下文”获取身份,例如 currentUser.userId。

请按以下顺序完成:

1. 先说明当前项目使用的后端框架、入口文件、配置文件和路由组织方式。
2. 新增认证模块,例如 auth_context / session_auth / middleware。
3. 修改环境变量示例文件和实际配置读取逻辑。
4. 把认证统一挂到业务 API 入口,不影响 `/health`。
5. 补充中文 README 或接入文档,写清楚环境变量、Docker 地址填写规则和用户上下文用法。
6. 增加最少的测试或可执行验证:
   - 无 Cookie 返回 401。
   - 不存在的 session_id 返回 401。
   - Redis Session 存在时,接口可读到 user.userId。
   - Redis 不可用时返回 503。
7. 最后列出修改的文件、接口行为和需要部署时配置的变量。

注意:当前任务只是“认证并注入用户上下文”。菜单/按钮级权限由后续网关或权限体系统一实现,当前 Agent 后端不要自行重新设计一套权限模型。

The user input with the bug:
首先把agent后端改为业务后端,其次,不要规定他的后续行为,也就是这些:

  • 将用户上下文注入当前请求生命周期。,然后:
  1. 健康检查接口 /health 可不鉴权;其余业务 API 默认全部鉴权。这个应该是说,做这个鉴权中间件,然后在入口层,除了健康检查其他api全部都走这个中间件,然后这一段什么玩意:

严格禁止:

  • 不处理 sso_code。
  • 不调用 SSO 服务。
  • 不解析、验证或存储 sso_token/JWT。
  • 不新增本地 token 体系。
  • 不使用 localStorage token。
  • 不自行创建、续期、删除 Redis Session。
  • 不修改 session:{session_id} 的内容或 TTL。
  • 不信任前端通过 Header、Body、Query 传来的 userId、角色或权限。
  • 不把 Redis 密码、完整 session_id、用户敏感信息写入日志。,你知道双重否定表肯定吗,然后什么叫认证注入上下文,注意:当前任务只是“认证并注入用户上下文”。菜单/按钮级权限由后续网关或权限体系统一实现,当前 Agent 后端不要自行重新设计一套权限模型。,我们当前任务只是从redis里面拿信息,后续怎么用,我们不要规定,这是同事自己决定的,我们只需要帮助他们实现鉴权拿userinfo这个中间件

View original on GitHub ↗

7 Comments

github-actions[bot] contributor · 10 days ago

English translation:

What version of the Codex App are you using (From “About Codex” dialog)?

ChatGPT Powered by Codex & OWL version 26.707.31428, released on July 10, 2026 (Due to the merger of Codex and ChatGPT today, only the ChatGPT version can be viewed.)

What subscription do you have?

gpt plus

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64

What issue are you seeing?

Phenomenon:

  • The problem is triggered by a user input message. This user input is automatically displayed by Codex as "Expand More", and it repeatedly expands and collapses when scrolling near this message, causing the scroll bar of the entire task conversation to continuously jump.
  • Switching to other tasks and then returning to this one, it is temporarily normal before rolling to the vicinity of this message.
  • Once scrolling to the vicinity of this extremely long reply, the jumping immediately begins.
  • Restarting Codex after this problem still exists because reloading the same task history will trigger it again.
  • This is not caused by the project web page or code, but a rendering issue of the conversation UI in the Codex Desktop App.

Reproduction steps:

  1. Open a project in the Codex Desktop App that already contains a long historical conversation, then input a conversation that may contain a special format / long Markdown / long code block.
  2. Scroll near the input of this conversation.
  3. The conversation (user input) shows an "Expand More" button, and "Expand More" repeatedly expands and collapses.
  4. Each height change triggers the recalculation of the scroll anchor, causing the scroll bar to continuously jump, making the entire conversation page unstable and constantly jumping.

Attempts:

  • Returning to the problem after switching tasks still exists
  • Completely closing and reopening Codex still exists
  • Attempting to open a new conversation input and copy the original conversation content, unable to reproduce, and no "Expand More" option appeared
  • In the current conversation with the bug, attempts were also made to input data with bugs, but the issue could not be reproduced either.
  • Let Codex output first. Before the input with the bug occurs, the content it replies with will be used. Then, when the user inputs the input with the bug, it cannot be reproduced.

Guess:
This conversation was previously discussing writing a prompt with Codex. The replies before and after this conversation all contain long Markdown, and multiple "-" characters caused this user input to have the "Expand More" button and be incorrectly repeatedly expanded and collapsed.

Additionally, I have reviewed the similar issues that were previously raised and none of them have been resolved but were directly closed. Please pay attention to this problem as it has occurred multiple times.

What steps can reproduce the bug?

Reproduction steps:

  1. Open a project in the Codex Desktop App that already contains a long historical conversation, then input a conversation that may contain a special format / long Markdown / long code block.
  2. Scroll near the input of this conversation.
  3. The conversation (user input) shows an "Expand More" button, and "Expand More" repeatedly expands and collapses.
  4. Each height change triggers the recalculation of the scroll anchor, causing the scroll bar to continuously jump, making the entire conversation page unstable and constantly jumping.

What is the expected behavior?

_No response_

Additional information

Before the user's input, the response content from Codex:
Send the entire section below directly to your colleague's AI:

You are integrating a unified login session into an existing Agent business backend.

First review the current project structure, startup method, and dependency list, and then modify the code. Do not run npm install, pip install, or any other installation command. If dependencies are needed, only modify the dependency declaration file and explain what needs to be installed.

Unified authentication architecture:

- After the frontend logs in, the unified Auth Server writes the login session to Redis.
- The browser holds only the HttpOnly Cookie: session_id.
- Redis is the sole source of login session data.
- Redis key format: session:{session_id}
- The Redis value is JSON with approximately the following overall structure:

{
  "schemaVersion": 2,
  "user": {
    "userId": "Unique user identifier",
    "userLoginName": "Login name",
    "userName": "Name",
    "email": "Email",
    "ip": "IP",
    "domainId": "Domain ID",
    "orgLevel3Name": "Third-level name of the user's organization",
    "iat": 0,
    "exp": 0,
    "crossToken": null,
    "authMode": "SSO"
  },
  "access": {
    "isSuperAdmin": false,
    "globalRoles": [
      {
        "roleId": "Role ID",
        "roleName": "Role name",
        "roleCode": "Role code"
      }
    ],
    "globalMenus": [
      {
        "menuId": "Menu ID",
        "menuName": "Menu name",
        "permissionCode": "Permission code",
        "routePath": "Route",
        "parentId": null,
        "visible": true,
        "sortOrder": 0
      }
    ]
  }
}

Objective:

Add a unified authentication entry point to all business endpoints in the current Agent backend:

1. Read session_id from the request Cookie.
2. Query session:{session_id} in Redis.
3. If the Redis Session exists and contains valid JSON:
   - Obtain userInfo, namely session.user.
   - The role and menu information in session.access can also be obtained.
   - Inject the user context into the lifecycle of the current request.
   - Subsequent business code can directly obtain the current user, rather than receiving userId through request parameters, Headers, or the frontend.
4. If the Cookie does not exist, the Session does not exist, the Session has expired, or the JSON is invalid:
   - Return HTTP 401 Unauthorized.
5. If the Redis connection fails:
   - Return HTTP 503 Service Unavailable.
   - Do not disguise a Redis failure as a 401.
6. The health-check endpoint `/health` may be exempt from authentication; all other business APIs must be authenticated by default.

Strictly prohibited:

- Do not process sso_code.
- Do not call the SSO service.
- Do not parse, validate, or store sso_token/JWT.
- Do not add a local token system.
- Do not use a localStorage token.
- Do not independently create, renew, or delete Redis Sessions.
- Do not modify the contents or TTL of session:{session_id}.
- Do not trust user IDs, roles, or permissions sent by the frontend through Headers, Body, or Query parameters.
- Do not write Redis passwords, complete session_id values, or sensitive user information to logs.

Configuration requirements:

Add environment variables to the current service. The following standard names may be used:

REDIS_URL=redis://:your-Redis-password@Redis-host-IP:Redis-mapped-port/0
AUTH_SESSION_COOKIE_NAME=session_id
AUTH_SESSION_KEY_PREFIX=session:

Notes:

- REDIS_URL must refer to the same Redis instance and the same DB number used by the unified Auth Server.
- If the current Agent backend runs in a Docker container, 127.0.0.1 cannot be used as the Redis address.
- If the current deployment uses the “host IP + Redis mapped port” approach, enter the host's actual IP.
- If it is changed to a private Docker network in the future, REDIS_URL can then be changed to the Redis container service name, for example redis://:password@redis-container:6379/0.
- Do not write the real password into the code or Dockerfile or commit it to the repository.
- Configure it in the project's existing production environment file, such as agent_xxx.prod.env; do not require the filename .env.

Implementation requirements:

- Implement authentication using the global middleware, route guard, dependency injection, or interceptor recommended by the current project framework.
- For FastAPI, using Depends or router-level dependencies is recommended.
- For Node.js / TypeScript, use the corresponding global middleware or Guard for Express / NestJS / Koa.
- Do not require every business endpoint to manually repeat the Redis query.
- The request context must consistently provide at least:
  - userId
  - userLoginName
  - userName
  - email
  - domainId
  - orgLevel3Name
  - roles
  - isSuperAdmin
  - menus
- Business endpoints obtain identity through the “current user context,” for example currentUser.userId.

Complete the work in the following order:

1. First explain the backend framework, entry file, configuration files, and route organization used by the current project.
2. Add an authentication module, such as auth_context / session_auth / middleware.
3. Modify the environment-variable example file and the actual configuration-reading logic.
4. Attach authentication uniformly to the business API entry point without affecting `/health`.
5. Add a Chinese README or integration document clearly describing the environment variables, Docker address configuration rules, and use of the user context.
6. Add minimal tests or executable verification:
   - No Cookie returns 401.
   - A nonexistent session_id returns 401.
   - When the Redis Session exists, the endpoint can read user.userId.
   - When Redis is unavailable, return 503.
7. Finally, list the modified files, endpoint behavior, and variables that must be configured during deployment.

Note: The current task is only “authentication and injecting the user context.” Menu/button-level permissions will be implemented later by the gateway or unified permission system. The current Agent backend must not redesign its own permission model.

The user input with the bug:
First, change “Agent backend” to “business backend.” Second, do not prescribe their subsequent behavior, meaning these parts:

  • Inject the user context into the lifecycle of the current request. And then:
  1. The health-check endpoint /health may be exempt from authentication; all other business APIs must be authenticated by default. This should say to build this authentication middleware and then, at the entry layer, have all APIs except the health check go through this middleware. And what is this section supposed to mean:

Strictly prohibited:

  • Do not process sso_code.
  • Do not call the SSO service.
  • Do not parse, validate, or store sso_token/JWT.
  • Do not add a local token system.
  • Do not use a localStorage token.
  • Do not independently create, renew, or delete Redis Sessions.
  • Do not modify the contents or TTL of session:{session_id}.
  • Do not trust user IDs, roles, or permissions sent by the frontend through Headers, Body, or Query parameters.
  • Do not write Redis passwords, complete session_id values, or sensitive user information to logs. Do you understand that a double negative means an affirmative? And what does “authentication and injecting context” mean? Note: The current task is only “authentication and injecting the user context.” Menu/button-level permissions will be implemented later by the gateway or unified permission system. The current Agent backend must not redesign its own permission model. Our current task is only to retrieve information from Redis. We should not prescribe how it will be used afterward; that is for our colleagues to decide. We only need to help them implement middleware that performs authentication and retrieves userInfo.

<!-- codex-issue-translator -->

github-actions[bot] contributor · 10 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #32016

Powered by Codex Action

shiyo6926 · 10 days ago

Additional observation:

The scrollbar loop appears to be caused by a long message's “Show more” control repeatedly expanding and collapsing while that message is near the viewport.

Each expand/collapse changes the rendered message height, then the conversation scroll position is recalculated, causing the scrollbar and viewport to jump continuously.

This can happen in an idle thread with no active generation. Switching away and back temporarily avoids the issue until scrolling reaches the affected message again.

Related reports:
#28718
#27168
#28755
#32016
#18109

xmaayy · 10 days ago

Minimal message I've found to cause the issue

```user@host folder % command
Line
Line
Line
Line

Line
Line
Line

Line
Line
Line 
- list
- list
- list
- list
- ```

https://github.com/user-attachments/assets/81f41470-034e-4241-87bf-644c9f9b2855

winoooops · 9 days ago

I can also reproduce this on macOS 26.5.1 arm64 with Codex 26.707.30751.

Trigger: a long user message containing fenced code and lists in a narrow transcript (~529 pixel). Without input, it alternates between the clamped Show more state and full rendering about five times per second, causing large scroll jumps. The header and composer remain stable.

Codex logged ResizeObserver loop completed with undelivered notifications error. The errors drop while Review hides the transcript and resume when the thread returns.

Renderer inspection suggests a feedback loop: the same Markdown node is observed/measured via scrollHeight and conditionally given a 20-line -webkit-line-clamp. Clamping changes its geometry and retriggers the observer; scroll anchoring amplifies each height change.

Suggested fix: measure an always-unclamped inner Markdown node and clip only an outer wrapper. A regression test at a narrow width should verify stable message height, stable scrollTop, and zero observer-loop errors.

shiyo6926 · 8 days ago

Update: I used Codex to debug Codex 😄

Good news: with substantial help from OpenAI Codex itself, I was able to independently reproduce this issue, identify the renderer feedback loop, and validate a small mitigation prototype.

So yes — I used Codex to debug (and prototype a fix for) a Codex bug :D

TL;DR

The root cause is a self-triggering layout feedback loop caused by measuring and modifying the same DOM node.

The cycle:

  1. Codex measures a user message using element.scrollHeight to decide whether it should be collapsible.
  2. When the message becomes collapsed, it applies max-height and -webkit-line-clamp: 20 to that same measured Markdown node.
  3. For the malformed fenced-code/list structure in the minimal reproduction, applying the clamp changes the reported height from roughly 424–441 px down to 34 px.
  4. The collapse logic then determines that the message is no longer collapsible and removes the clamp.
  5. The original height returns, making the message collapsible again.
  6. The cycle repeats indefinitely.

The transcript scroll anchoring / virtualization layer then amplifies this message-height oscillation into the large scrollbar jumps reported in this issue.

This matches the independent macOS analysis in #32030 and explains the alternating full / normal / Show more frames reported in #32016.

---

Renderer-level evidence

I inspected the official packaged Electron application (app.asar) and found the relevant implementation path.

This was not native executable decompilation. An Electron ASAR is a resource archive containing already-built JavaScript/CSS bundles.

Tested package:

  • Windows package: OpenAI.Codex 26.707.3748.0
  • Bundled Electron app: 26.707.31428, build 5059
  • Electron: 42.1.0

Relevant bundled modules:

  • use-measured-text-collapse-*.js
  • use-resize-observer-*.js
  • generated-image-preview-*.js
  • user-formatted-text-*.js

The important finding is that measurement and presentation are coupled.

---

Collapse measurement

The packaged use-measured-text-collapse hook calculates:

contentHeightPx: Math.ceil(element.scrollHeight)

and decides the collapse state based on the measured height:

contentHeightPx <= collapsedHeightPx + threshold

Effectively:

content height <= collapsed height
        |
        v
   uncollapsible

content height > collapsed height
        |
        v
    collapsed

---

Same node receives the collapsed style

The renderer passes the measurement ref to the Markdown node and also applies the collapsed style to that same node:

{
  maxHeight: `${collapsedLineCount}lh`,
  WebkitLineClamp: collapsedLineCount
}

This means the value used to decide whether collapsing is needed is directly changed by the collapse presentation itself.

The resulting feedback loop is:

collapsed
   |
   v
WebkitLineClamp applied
   |
   v
scrollHeight decreases
   |
   v
collapse state becomes uncollapsible
   |
   v
clamp removed
   |
   v
scrollHeight restored
   |
   v
collapsed again

---

ResizeObserver behavior

I also inspected the bundled use-resize-observer implementation.

The observer forwards resize entries directly to registered callbacks:

new ResizeObserver(entries => {
    for (const entry of entries) {
        callbacks.forEach(callback => callback(entry));
    }
})

There is no additional stabilization layer between the DOM size change and the measurement callback.

Therefore:

style change
    ↓
DOM size change
    ↓
ResizeObserver callback
    ↓
recalculate scrollHeight
    ↓
collapse state update
    ↓
style change again

This explains why the loop can continue while the conversation is otherwise idle.

---

Isolated A/B validation

I reproduced the behavior using an isolated copy of the packaged application.

The test used:

  • packaged UserFormattedText
  • packaged useMeasuredTextCollapse
  • shared ResizeObserver
  • packaged CSS
  • the exact minimal reproduction

Configuration:

  • outer width: 529 px
  • measured node width: 497 px

No installed WindowsApps package was modified, no credentials were copied, and no real signed-in session was used.

| Four-second run | ResizeObserver callbacks | Collapse-state records | Result |
| -------------------------------------------------------------- | -----------------------: | ---------------------: | ------------------------------------------- |
| Current WebkitLineClamp: 20 behavior | 558 | 557 | Continuous 34 px ↔ 424/441 px oscillation |
| Same behavior with only the WebkitLineClamp mutation removed | 4 | 2 startup states | Stable collapsed state |

Control run:

uncollapsible: 279
collapsed: 278

Mitigation run:

uncollapsible
      ↓
collapsed

and then remained stable.

The isolated harness validates the message-height feedback loop itself. It does not attempt to reproduce the complete signed-in transcript virtualization/scroll-anchor layer, which is already demonstrated by the recordings attached to this issue.

---

Suggested fix

The robust fix is to separate measurement from presentation.

Instead of measuring and clipping the same element:

  • keep an inner Markdown node permanently unclamped;
  • attach the measurement ref / ResizeObserver to the inner node;
  • wrap it in an outer container;
  • apply max-height and overflow: hidden only to the outer wrapper;
  • optionally use a fade/overlay affordance instead of line-clamp for arbitrary Markdown blocks.

This keeps the measured height independent from the collapsed visual state.

As an emergency mitigation, removing only:

WebkitLineClamp

while keeping:

max-height
overflow: hidden

and the existing Show more/less control stopped the oscillation in the A/B test.

---

Why I am not opening a PR

I also searched the current public openai/codex repository.

The Electron/webview React UI containing these components does not appear to be present there.

The public repository contains the CLI, Rust core/app-server/TUI, and SDK, but not the desktop renderer implementation involved here.

Therefore, a community PR against the public repository would not be meaningful for this specific fix.

The attached prototype is only a proof of concept against the packaged bundle. It is not intended as a production patch, installer, or replacement for the internal source implementation.

I am deliberately not attaching extracted OpenAI bundle code, the original ASAR, or a patched ASAR.

---

Investigation artifacts

I prepared a small investigation bundle containing only my own report and test material:

  • technical investigation report;
  • structured A/B results;
  • version-locked proof-of-concept mitigation script;
  • this issue comment draft.

The archive contains:

  • no extracted OpenAI JavaScript;
  • no original ASAR;
  • no patched ASAR;
  • no credentials;
  • no user/session data.

Attachment:

codex-32030-investigation-artifacts.zip

---

A friendly nudge to the OpenAI team 🙏

This issue reproduces on both Windows and macOS, can make affected conversations effectively unusable, and may continue consuming CPU/GPU resources while the thread is idle.

At this point we have:

  • a deterministic minimal reproduction;
  • renderer-level root cause;
  • source-level implementation evidence;
  • cross-platform confirmation;
  • quantitative A/B validation;
  • a minimal mitigation;
  • a permanent fix direction;
  • and a regression-test plan.

Hopefully this investigation shortens the path to an internal fix.

Thank you to everyone who provided recordings, frame analysis, and additional platform reports. Those reports were genuinely useful in connecting the visible scrollbar jumping with the underlying message-height feedback loop.

---

AI-assistance disclosure

The investigation, bundle inspection tooling, experimental harness, mitigation prototype, and report were developed with substantial assistance from OpenAI Codex.

I directed the investigation, reviewed the relevant locations, and independently ran and verified the experiments locally.

In short:

Codex helped me locate a Codex bug.
Now we just need Codex's humans to merge the internal fix. 😄

codex-32030-investigation-artifacts.zip

shiyo6926 · 7 days ago

Good news! The version released in the early morning has fixed this bug. Now I can close this issue.