Snapshot tracking does not initialize per-project git directory, prevents undo/redo and leaves no snapshot data #2031

Closed
opened 2026-02-16 17:33:49 -05:00 by yindo · 19 comments
Owner

Originally created by @SsparKluo on GitHub (Oct 10, 2025).

Originally assigned to: @thdxr on GitHub.

Problem

When using opencode in a git repo, the snapshot feature appears to log tracking info (including a hash and git directory path), but no corresponding snapshot directory is created under the opencode data folder (e.g., ~/.local/share/opencode/snapshot/). As a result, undo/redo functionality does not work, and no snapshot data is stored.

Example log:

INFO  2025-10-10T07:34:36 +9ms service=snapshot hash= cwd=/app git=/home/node/.local/share/opencode/snapshot/ea1d6ee38fdccb1328af1d8956106e4a54389643 tracking

But no such snapshot folder, even the snapshot folder itself, exists in the file system.

Environment

  • opencode version: (latest/dev)
  • Running in container: Yes

The following part is analysis from github copilot

Analysis

  • The code in packages/opencode/src/snapshot/index.ts uses if (await fs.mkdir(...)) to conditionally run git init for the snapshot git directory. However, fs.mkdir resolves to undefined on success, so the condition is never true, and initialization does not run. This can leave the directory uninitialized or missing.
  • Subsequent snapshot/undo/redo operations fail silently or appear to succeed, but no data is actually stored.

Possible Solution

  • Remove the conditional around fs.mkdir and always attempt to initialize the git directory if not present.
  • See code analysis above for suggested patch.

Additional Context

  • Related code: packages/opencode/src/snapshot/index.ts
  • User log attached above.
Originally created by @SsparKluo on GitHub (Oct 10, 2025). Originally assigned to: @thdxr on GitHub. ### Problem When using opencode in a git repo, the snapshot feature appears to log tracking info (including a hash and git directory path), but no corresponding snapshot directory is created under the opencode data folder (e.g., ~/.local/share/opencode/snapshot/<project-id>). As a result, undo/redo functionality does not work, and no snapshot data is stored. #### Example log: ``` INFO 2025-10-10T07:34:36 +9ms service=snapshot hash= cwd=/app git=/home/node/.local/share/opencode/snapshot/ea1d6ee38fdccb1328af1d8956106e4a54389643 tracking ``` But no such snapshot folder, even the `snapshot` folder itself, exists in the file system. ### Environment - opencode version: (latest/dev) - Running in container: Yes --- The following part is analysis from github copilot ### Analysis - The code in `packages/opencode/src/snapshot/index.ts` uses `if (await fs.mkdir(...))` to conditionally run `git init` for the snapshot git directory. However, `fs.mkdir` resolves to `undefined` on success, so the condition is never true, and initialization does not run. This can leave the directory uninitialized or missing. - Subsequent snapshot/undo/redo operations fail silently or appear to succeed, but no data is actually stored. ### Possible Solution - Remove the conditional around `fs.mkdir` and always attempt to initialize the git directory if not present. - See code analysis above for suggested patch. ### Additional Context - Related code: `packages/opencode/src/snapshot/index.ts` - User log attached above.
yindo closed this issue 2026-02-16 17:33:49 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Oct 10, 2025):

This issue might be a duplicate of existing issues. Please check:

  • #2379: Very similar snapshot directory initialization issue with undo/redo functionality not working and snapshot folders being missing/deleted

Both issues appear to stem from the same root cause in snapshot service initialization where the snapshot git directories are not properly created or are being deleted during initialization.

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Oct 10, 2025): This issue might be a duplicate of existing issues. Please check: - #2379: Very similar snapshot directory initialization issue with undo/redo functionality not working and snapshot folders being missing/deleted Both issues appear to stem from the same root cause in snapshot service initialization where the snapshot git directories are not properly created or are being deleted during initialization. Feel free to ignore if none of these address your specific case.
Author
Owner

@OpeOginni commented on GitHub (Oct 10, 2025):

Hey @SsparKluo are you okay with sharing the Container File, im assuming you are using docker

@OpeOginni commented on GitHub (Oct 10, 2025): Hey @SsparKluo are you okay with sharing the Container File, im assuming you are using docker
Author
Owner

@SsparKluo commented on GitHub (Oct 10, 2025):

Hey @SsparKluo are you okay with sharing the Container File, im assuming you are using docker

I don't think it only happens in the container, but fyi, the container's image is built by

# syntax=docker/dockerfile:1
ARG NODE_VERSION=22.17
FROM node:${NODE_VERSION}-bullseye AS base

WORKDIR /app

# User mapping
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN set -eux; \
    apt-get update && apt-get install -y --no-install-recommends \
        sudo \
        gosu \
        && rm -rf /var/lib/apt/lists/*; \
    if getent group $GROUP_ID; then \
        groupname=$(getent group $GROUP_ID | cut -d: -f1); \
    else \
        groupname=appgroup; \
        groupadd -g $GROUP_ID $groupname; \
    fi; \
    if getent passwd $USER_ID; then \
        username=$(getent passwd $USER_ID | cut -d: -f1); \
    else \
        username=appuser; \
        useradd -u $USER_ID -m -s /bin/bash -g $groupname $username; \
    fi; \
    mkdir -p /etc/sudoers.d && \
    echo "$username ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$username" && \
    chmod 0440 "/etc/sudoers.d/$username"

# For development container
FROM base AS dev
ENV NODE_ENV=development
SHELL ["/bin/bash", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends \
    git openssh-client bash tree \
    curl wget vim nano htop unzip jq less \
    && rm -rf /var/lib/apt/lists/*

COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
ARG USER_ID=1000
ARG GROUP_ID=1000
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

EXPOSE 3001

CMD ["/bin/sh"]

and the compose yml is:

version: '3.8'
services:
  frontend:
    build:
      context: .
      target: dev
      args:
        USER_ID: ${USER_ID:-1000}
        GROUP_ID: ${GROUP_ID:-1000}
    volumes:
      - .:/app
      - node_modules:/app/node_modules
      - ~/.ssh:/home/appuser/.ssh:ro
      - npm_global_npm:/home/node/.npm-global
      - npm_global_config:/home/node/.config
      - npm_global_local:/home/node/.local
    ports:
      - "3001:3001"
    environment:
      - NODE_ENV=development
      - USER_ID=${USER_ID:-1000}
      - GROUP_ID=${GROUP_ID:-1000}


    tty: true
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  node_modules:
  npm_global_npm:
    external: true
  npm_global_config:
    external: true
  npm_global_local:
    external: true

But the reason why I say the problem is not docker related is that ... it also happens on the host, a debain on wsl.

demo

The gif shows my attempt to undo the message asking the agent write a numbered list in the demo.md. The project is in a git repo (although only init). The result is ... undo message shown, tool call "reverted", but no line delete info, and the file not reverted too.

I also cannot find snapshot folder in my ~/.local/share/opencode folder, and no snapshot info in the log. I also try to set snapshot to true in the config json, and it doesn't help

@SsparKluo commented on GitHub (Oct 10, 2025): > Hey [@SsparKluo](https://github.com/SsparKluo) are you okay with sharing the Container File, im assuming you are using docker I don't think it only happens in the container, but fyi, the container's image is built by ```dockerfile # syntax=docker/dockerfile:1 ARG NODE_VERSION=22.17 FROM node:${NODE_VERSION}-bullseye AS base WORKDIR /app # User mapping ARG USER_ID=1000 ARG GROUP_ID=1000 RUN set -eux; \ apt-get update && apt-get install -y --no-install-recommends \ sudo \ gosu \ && rm -rf /var/lib/apt/lists/*; \ if getent group $GROUP_ID; then \ groupname=$(getent group $GROUP_ID | cut -d: -f1); \ else \ groupname=appgroup; \ groupadd -g $GROUP_ID $groupname; \ fi; \ if getent passwd $USER_ID; then \ username=$(getent passwd $USER_ID | cut -d: -f1); \ else \ username=appuser; \ useradd -u $USER_ID -m -s /bin/bash -g $groupname $username; \ fi; \ mkdir -p /etc/sudoers.d && \ echo "$username ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$username" && \ chmod 0440 "/etc/sudoers.d/$username" # For development container FROM base AS dev ENV NODE_ENV=development SHELL ["/bin/bash", "-c"] RUN apt-get update && apt-get install -y --no-install-recommends \ git openssh-client bash tree \ curl wget vim nano htop unzip jq less \ && rm -rf /var/lib/apt/lists/* COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile ARG USER_ID=1000 ARG GROUP_ID=1000 COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] EXPOSE 3001 CMD ["/bin/sh"] ``` and the compose yml is: ```yaml version: '3.8' services: frontend: build: context: . target: dev args: USER_ID: ${USER_ID:-1000} GROUP_ID: ${GROUP_ID:-1000} volumes: - .:/app - node_modules:/app/node_modules - ~/.ssh:/home/appuser/.ssh:ro - npm_global_npm:/home/node/.npm-global - npm_global_config:/home/node/.config - npm_global_local:/home/node/.local ports: - "3001:3001" environment: - NODE_ENV=development - USER_ID=${USER_ID:-1000} - GROUP_ID=${GROUP_ID:-1000} tty: true logging: driver: "json-file" options: max-size: "10m" max-file: "3" volumes: node_modules: npm_global_npm: external: true npm_global_config: external: true npm_global_local: external: true ``` --- But the reason why I say the problem is not docker related is that ... it also happens on the host, a debain on wsl. ![demo](https://github.com/user-attachments/assets/a41610db-8116-43b4-93fb-584c1a301028) The gif shows my attempt to undo the message asking the agent write a numbered list in the demo.md. The project is in a git repo (although only init). The result is ... undo message shown, tool call "reverted", but no line delete info, and the file not reverted too. I also cannot find snapshot folder in my ~/.local/share/opencode folder, and no snapshot info in the log. I also try to set `snapshot` to true in the config json, and it doesn't help
Author
Owner

@rekram1-node commented on GitHub (Oct 10, 2025):

Ah so there is something going on with snapshots for you

@rekram1-node commented on GitHub (Oct 10, 2025): Ah so there is something going on with snapshots for you
Author
Owner

@rekram1-node commented on GitHub (Oct 10, 2025):

Can you show me the latest log file if u do a revert?

Logs are in ~/.local/share/opencode/log/

@rekram1-node commented on GitHub (Oct 10, 2025): Can you show me the latest log file if u do a revert? Logs are in ~/.local/share/opencode/log/
Author
Owner

@OpeOginni commented on GitHub (Oct 10, 2025):

@SsparKluo this might be a stretch but would it be possible to build your image using oven-sh/bun and see what happens?

@OpeOginni commented on GitHub (Oct 10, 2025): @SsparKluo this might be a stretch but would it be possible to build your image using `oven-sh/bun` and see what happens?
Author
Owner

@SsparKluo commented on GitHub (Oct 10, 2025):

Can you show me the latest log file if u do a revert?

Logs are in ~/.local/share/opencode/log/

INFO  2025-10-10T16:17:55 +102ms service=default version=0.14.7 args=["-c"] opencode
INFO  2025-10-10T16:17:55 +4ms service=project directory=/home/louis/workspace/tmp fromDirectory
INFO  2025-10-10T16:17:55 +18ms service=config path=/home/louis/.config/opencode/config.json loading
INFO  2025-10-10T16:17:55 +0ms service=config path=/home/louis/.config/opencode/opencode.json loading
INFO  2025-10-10T16:17:55 +1ms service=config path=/home/louis/.config/opencode/opencode.jsonc loading
INFO  2025-10-10T16:17:55 +21ms service=plugin path=opencode-copilot-auth@0.0.3 loading plugin
INFO  2025-10-10T16:17:55 +5ms service=plugin path=opencode-anthropic-auth@0.0.2 loading plugin
INFO  2025-10-10T16:17:55 +31ms service=bus type=* subscribing
INFO  2025-10-10T16:17:55 +0ms service=bus type=session.updated subscribing
INFO  2025-10-10T16:17:55 +0ms service=bus type=message.updated subscribing
INFO  2025-10-10T16:17:55 +0ms service=bus type=message.part.updated subscribing
INFO  2025-10-10T16:17:55 +0ms service=format init
INFO  2025-10-10T16:17:55 +0ms service=bus type=file.edited subscribing
INFO  2025-10-10T16:17:55 +15ms service=lsp serverIds=typescript, vue, eslint, gopls, ruby-lsp, pyright, elixir-ls, zls, csharp, rust, clangd, svelte, jdtls enabled LSP servers
INFO  2025-10-10T16:17:55 +7ms service=models.dev file={} refreshing
INFO  2025-10-10T16:17:55 +3ms service=provider init
INFO  2025-10-10T16:17:55 +2ms service=provider providerID=modelscope found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=google found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=cerebras found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=deepseek found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=zhipuai found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=zai-coding-plan found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=opencode found
INFO  2025-10-10T16:17:55 +0ms service=provider providerID=github-copilot found
INFO  2025-10-10T16:17:55 +12ms service=default cmd=["/home/louis/.cache/opencode/tui/tui-42nmq6m0."] tui
INFO  2025-10-10T16:17:59 +4086ms service=server method=GET path=/agent request
INFO  2025-10-10T16:17:59 +2ms service=server duration=2 response
INFO  2025-10-10T16:17:59 +1ms service=server method=GET path=/project/current request
INFO  2025-10-10T16:17:59 +0ms service=server duration=0 response
INFO  2025-10-10T16:17:59 +0ms service=server method=GET path=/path request
INFO  2025-10-10T16:17:59 +0ms service=server duration=0 response
INFO  2025-10-10T16:17:59 +3ms service=server method=GET path=/config request
INFO  2025-10-10T16:17:59 +1ms service=server duration=1 response
INFO  2025-10-10T16:17:59 +7ms service=server method=GET path=/command request
INFO  2025-10-10T16:17:59 +0ms service=server duration=0 response
INFO  2025-10-10T16:17:59 +1ms service=server method=GET path=/file/status request
INFO  2025-10-10T16:17:59 +1ms service=server duration=1 response
INFO  2025-10-10T16:17:59 +2ms service=server method=GET path=/event request
INFO  2025-10-10T16:17:59 +0ms service=server event connected
INFO  2025-10-10T16:17:59 +2ms service=bus type=* subscribing
INFO  2025-10-10T16:17:59 +0ms service=server duration=2 response
INFO  2025-10-10T16:17:59 +1ms service=server method=GET path=/tui/control/next request
INFO  2025-10-10T16:17:59 +3ms service=server method=GET path=/config/providers request
INFO  2025-10-10T16:17:59 +1ms service=server duration=1 response
INFO  2025-10-10T16:17:59 +2ms service=tui commands={"agent_cycle":{},"agent_cycle_reverse":{},"agent_list":{},"app_exit":{},"app_help":{},"editor_open":{},"input_clear":{},"input_newline":{},"input_paste":{},"input_submit":{},"messages_copy":{},"messages_first":{},"messages_half_page_down":{},"messages_half_page_up":{},"messages_last":{},"messages_page_down":{},"messages_page_up":{},"messages_redo":{},"messages_undo":{},"model_cycle_recent":{},"model_cycle_recent_reverse":{},"model_list":{},"project_init":{},"session_child_cycle":{},"session_child_cycle_reverse":{},"session_compact":{},"session_export":{},"session_interrupt":{},"session_list":{},"session_new":{},"session_share":{},"session_timeline":{},"session_unshare":{},"theme_list":{},"thinking_blocks":{},"tool_details":{}} Loaded commands
INFO  2025-10-10T16:17:59 +19ms service=server method=GET path=/session request
INFO  2025-10-10T16:17:59 +24ms service=server duration=24 response
INFO  2025-10-10T16:17:59 +2ms service=server method=GET path=/session/ses_6313ea928ffe2IciiSQrskl8Vb/message request
INFO  2025-10-10T16:17:59 +17ms service=server duration=17 response
INFO  2025-10-10T16:18:02 +3128ms service=server method=POST path=/session/ses_6313ea928ffe2IciiSQrskl8Vb/revert request
INFO  2025-10-10T16:18:02 +1ms service=server messageID=msg_9ced49094001gdttDU8qBrwLuA revert
INFO  2025-10-10T16:18:02 +7ms service=bus type=session.updated publishing
INFO  2025-10-10T16:18:02 +1ms service=server duration=9 response
@SsparKluo commented on GitHub (Oct 10, 2025): > Can you show me the latest log file if u do a revert? > > Logs are in ~/.local/share/opencode/log/ ``` INFO 2025-10-10T16:17:55 +102ms service=default version=0.14.7 args=["-c"] opencode INFO 2025-10-10T16:17:55 +4ms service=project directory=/home/louis/workspace/tmp fromDirectory INFO 2025-10-10T16:17:55 +18ms service=config path=/home/louis/.config/opencode/config.json loading INFO 2025-10-10T16:17:55 +0ms service=config path=/home/louis/.config/opencode/opencode.json loading INFO 2025-10-10T16:17:55 +1ms service=config path=/home/louis/.config/opencode/opencode.jsonc loading INFO 2025-10-10T16:17:55 +21ms service=plugin path=opencode-copilot-auth@0.0.3 loading plugin INFO 2025-10-10T16:17:55 +5ms service=plugin path=opencode-anthropic-auth@0.0.2 loading plugin INFO 2025-10-10T16:17:55 +31ms service=bus type=* subscribing INFO 2025-10-10T16:17:55 +0ms service=bus type=session.updated subscribing INFO 2025-10-10T16:17:55 +0ms service=bus type=message.updated subscribing INFO 2025-10-10T16:17:55 +0ms service=bus type=message.part.updated subscribing INFO 2025-10-10T16:17:55 +0ms service=format init INFO 2025-10-10T16:17:55 +0ms service=bus type=file.edited subscribing INFO 2025-10-10T16:17:55 +15ms service=lsp serverIds=typescript, vue, eslint, gopls, ruby-lsp, pyright, elixir-ls, zls, csharp, rust, clangd, svelte, jdtls enabled LSP servers INFO 2025-10-10T16:17:55 +7ms service=models.dev file={} refreshing INFO 2025-10-10T16:17:55 +3ms service=provider init INFO 2025-10-10T16:17:55 +2ms service=provider providerID=modelscope found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=google found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=cerebras found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=deepseek found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=zhipuai found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=zai-coding-plan found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=opencode found INFO 2025-10-10T16:17:55 +0ms service=provider providerID=github-copilot found INFO 2025-10-10T16:17:55 +12ms service=default cmd=["/home/louis/.cache/opencode/tui/tui-42nmq6m0."] tui INFO 2025-10-10T16:17:59 +4086ms service=server method=GET path=/agent request INFO 2025-10-10T16:17:59 +2ms service=server duration=2 response INFO 2025-10-10T16:17:59 +1ms service=server method=GET path=/project/current request INFO 2025-10-10T16:17:59 +0ms service=server duration=0 response INFO 2025-10-10T16:17:59 +0ms service=server method=GET path=/path request INFO 2025-10-10T16:17:59 +0ms service=server duration=0 response INFO 2025-10-10T16:17:59 +3ms service=server method=GET path=/config request INFO 2025-10-10T16:17:59 +1ms service=server duration=1 response INFO 2025-10-10T16:17:59 +7ms service=server method=GET path=/command request INFO 2025-10-10T16:17:59 +0ms service=server duration=0 response INFO 2025-10-10T16:17:59 +1ms service=server method=GET path=/file/status request INFO 2025-10-10T16:17:59 +1ms service=server duration=1 response INFO 2025-10-10T16:17:59 +2ms service=server method=GET path=/event request INFO 2025-10-10T16:17:59 +0ms service=server event connected INFO 2025-10-10T16:17:59 +2ms service=bus type=* subscribing INFO 2025-10-10T16:17:59 +0ms service=server duration=2 response INFO 2025-10-10T16:17:59 +1ms service=server method=GET path=/tui/control/next request INFO 2025-10-10T16:17:59 +3ms service=server method=GET path=/config/providers request INFO 2025-10-10T16:17:59 +1ms service=server duration=1 response INFO 2025-10-10T16:17:59 +2ms service=tui commands={"agent_cycle":{},"agent_cycle_reverse":{},"agent_list":{},"app_exit":{},"app_help":{},"editor_open":{},"input_clear":{},"input_newline":{},"input_paste":{},"input_submit":{},"messages_copy":{},"messages_first":{},"messages_half_page_down":{},"messages_half_page_up":{},"messages_last":{},"messages_page_down":{},"messages_page_up":{},"messages_redo":{},"messages_undo":{},"model_cycle_recent":{},"model_cycle_recent_reverse":{},"model_list":{},"project_init":{},"session_child_cycle":{},"session_child_cycle_reverse":{},"session_compact":{},"session_export":{},"session_interrupt":{},"session_list":{},"session_new":{},"session_share":{},"session_timeline":{},"session_unshare":{},"theme_list":{},"thinking_blocks":{},"tool_details":{}} Loaded commands INFO 2025-10-10T16:17:59 +19ms service=server method=GET path=/session request INFO 2025-10-10T16:17:59 +24ms service=server duration=24 response INFO 2025-10-10T16:17:59 +2ms service=server method=GET path=/session/ses_6313ea928ffe2IciiSQrskl8Vb/message request INFO 2025-10-10T16:17:59 +17ms service=server duration=17 response INFO 2025-10-10T16:18:02 +3128ms service=server method=POST path=/session/ses_6313ea928ffe2IciiSQrskl8Vb/revert request INFO 2025-10-10T16:18:02 +1ms service=server messageID=msg_9ced49094001gdttDU8qBrwLuA revert INFO 2025-10-10T16:18:02 +7ms service=bus type=session.updated publishing INFO 2025-10-10T16:18:02 +1ms service=server duration=9 response ```
Author
Owner

@SsparKluo commented on GitHub (Oct 10, 2025):

@SsparKluo this might be a stretch but would it be possible to build your image using oven-sh/bun and see what happens?

do you means use bun instead of yarn in dockerfile? but i actually didn't build a opencode..., just install it by npm -g, and it's installed to the mounted volume (I changee the npm global path in the container)

@SsparKluo commented on GitHub (Oct 10, 2025): > [@SsparKluo](https://github.com/SsparKluo) this might be a stretch but would it be possible to build your image using `oven-sh/bun` and see what happens? do you means use bun instead of yarn in dockerfile? but i actually didn't build a opencode..., just install it by npm -g, and it's installed to the mounted volume (I changee the npm global path in the container)
Author
Owner

@rekram1-node commented on GitHub (Oct 10, 2025):

@OpeOginni hey this isn't an image issue :)

@rekram1-node commented on GitHub (Oct 10, 2025): @OpeOginni hey this isn't an image issue :)
Author
Owner

@SsparKluo commented on GitHub (Oct 12, 2025):

I can reproduce it with a newly install a new opencode in a new wsl distro, Ubuntu 20.04, installed by curl -fsSL https://opencode.ai/install | bash.

mkdir, cd, git init. and use opencode to create any file, like create a demo md here, and then /undo. It showed reverted, but the file still exists.

@SsparKluo commented on GitHub (Oct 12, 2025): I can reproduce it with a newly install a new opencode in a new wsl distro, Ubuntu 20.04, installed by `curl -fsSL https://opencode.ai/install | bash`. mkdir, cd, git init. and use opencode to create any file, like `create a demo md here`, and then /undo. It showed reverted, but the file still exists.
Author
Owner

@rekram1-node commented on GitHub (Oct 12, 2025):

if there is a failure there should be error logs

ill just have to read over code for some reason it breaks on your machine, maybe adding more logging would help

ill get back to u on this

@rekram1-node commented on GitHub (Oct 12, 2025): if there is a failure there should be error logs ill just have to read over code for some reason it breaks on your machine, maybe adding more logging would help ill get back to u on this
Author
Owner

@SsparKluo commented on GitHub (Oct 12, 2025):

Agree... but cannot find any error in the log, that's weird.

I can reproduce it with a newly install a new opencode in a new wsl distro, Ubuntu 20.04, installed by curl -fsSL https://opencode.ai/install | bash.

mkdir, cd, git init. and use opencode to create any file, like create a demo md here, and then /undo. It showed reverted, but the file still exists.

here is the part of log for revert and unrevert:

INFO  2025-10-12T16:57:17 +1ms service=session.prompt session=ses_626a53519ffeOu3kQm5EEX6r4H sessionID=ses_626a53519ffeOu3kQm5EEX6r4H unlocking
INFO  2025-10-12T16:57:17 +0ms service=server duration=7017 response
INFO  2025-10-12T16:57:17 +0ms service=bus type=session.idle publishing
INFO  2025-10-12T16:57:17 +2ms service=session.compaction pruned=0 total=0 found
INFO  2025-10-12T16:57:23 +6041ms service=server method=POST path=/session/ses_626a53519ffeOu3kQm5EEX6r4H/revert request
INFO  2025-10-12T16:57:23 +1ms service=server messageID=msg_9d95acae9001ZQILqGm8XaRfVo revert
INFO  2025-10-12T16:57:23 +6ms service=snapshot hash= cwd=/home/louis/Documents/tmp git=/home/louis/.local/share/opencode/snapshot/89fb41f888ba2867409889c2fbea38fb2041ecef tracking
INFO  2025-10-12T16:57:23 +1ms service=bus type=session.updated publishing
INFO  2025-10-12T16:57:23 +0ms service=server duration=8 response
INFO  2025-10-12T16:57:28 +5319ms service=server method=POST path=/session/ses_626a53519ffeOu3kQm5EEX6r4H/unrevert request
INFO  2025-10-12T16:57:28 +1ms service=session.revert sessionID=ses_626a53519ffeOu3kQm5EEX6r4H unreverting
INFO  2025-10-12T16:57:28 +0ms service=bus type=session.updated publishing
INFO  2025-10-12T16:57:28 +0ms service=server duration=1 response
INFO  2025-10-12T16:57:57 +29379ms service=server method=POST path=/session/ses_626a53519ffeOu3kQm5EEX6r4H/revert request
INFO  2025-10-12T16:57:57 +1ms service=server messageID=msg_9d95acae9001ZQILqGm8XaRfVo revert
INFO  2025-10-12T16:57:57 +5ms service=snapshot hash= cwd=/home/louis/Documents/tmp git=/home/louis/.local/share/opencode/snapshot/89fb41f888ba2867409889c2fbea38fb2041ecef tracking
INFO  2025-10-12T16:57:57 +1ms service=bus type=session.updated publishing
INFO  2025-10-12T16:57:57 +0ms service=server duration=7 response
INFO  2025-10-12T16:58:31 +33115ms service=bus type=* unsubscribing
INFO  2025-10-12T16:58:31 +0ms service=server event disconnected

looks fine ... but nothing in /home/louis/.local/share/opencode/snapshot/89fb41f888ba2867409889c2fbea38fb2041ecef (although the path exist this time)

Just wonder why this doesn't effect others... Or is there any other requirement except the initlized git repo.

@SsparKluo commented on GitHub (Oct 12, 2025): Agree... but cannot find any error in the log, that's weird. > I can reproduce it with a newly install a new opencode in a new wsl distro, Ubuntu 20.04, installed by `curl -fsSL https://opencode.ai/install | bash`. > > mkdir, cd, git init. and use opencode to create any file, like `create a demo md here`, and then /undo. It showed reverted, but the file still exists. here is the part of log for revert and unrevert: ``` INFO 2025-10-12T16:57:17 +1ms service=session.prompt session=ses_626a53519ffeOu3kQm5EEX6r4H sessionID=ses_626a53519ffeOu3kQm5EEX6r4H unlocking INFO 2025-10-12T16:57:17 +0ms service=server duration=7017 response INFO 2025-10-12T16:57:17 +0ms service=bus type=session.idle publishing INFO 2025-10-12T16:57:17 +2ms service=session.compaction pruned=0 total=0 found INFO 2025-10-12T16:57:23 +6041ms service=server method=POST path=/session/ses_626a53519ffeOu3kQm5EEX6r4H/revert request INFO 2025-10-12T16:57:23 +1ms service=server messageID=msg_9d95acae9001ZQILqGm8XaRfVo revert INFO 2025-10-12T16:57:23 +6ms service=snapshot hash= cwd=/home/louis/Documents/tmp git=/home/louis/.local/share/opencode/snapshot/89fb41f888ba2867409889c2fbea38fb2041ecef tracking INFO 2025-10-12T16:57:23 +1ms service=bus type=session.updated publishing INFO 2025-10-12T16:57:23 +0ms service=server duration=8 response INFO 2025-10-12T16:57:28 +5319ms service=server method=POST path=/session/ses_626a53519ffeOu3kQm5EEX6r4H/unrevert request INFO 2025-10-12T16:57:28 +1ms service=session.revert sessionID=ses_626a53519ffeOu3kQm5EEX6r4H unreverting INFO 2025-10-12T16:57:28 +0ms service=bus type=session.updated publishing INFO 2025-10-12T16:57:28 +0ms service=server duration=1 response INFO 2025-10-12T16:57:57 +29379ms service=server method=POST path=/session/ses_626a53519ffeOu3kQm5EEX6r4H/revert request INFO 2025-10-12T16:57:57 +1ms service=server messageID=msg_9d95acae9001ZQILqGm8XaRfVo revert INFO 2025-10-12T16:57:57 +5ms service=snapshot hash= cwd=/home/louis/Documents/tmp git=/home/louis/.local/share/opencode/snapshot/89fb41f888ba2867409889c2fbea38fb2041ecef tracking INFO 2025-10-12T16:57:57 +1ms service=bus type=session.updated publishing INFO 2025-10-12T16:57:57 +0ms service=server duration=7 response INFO 2025-10-12T16:58:31 +33115ms service=bus type=* unsubscribing INFO 2025-10-12T16:58:31 +0ms service=server event disconnected ``` looks fine ... but nothing in `/home/louis/.local/share/opencode/snapshot/89fb41f888ba2867409889c2fbea38fb2041ecef` (although the path exist this time) Just wonder why this doesn't effect others... Or is there any other requirement except the initlized git repo.
Author
Owner

@rekram1-node commented on GitHub (Oct 12, 2025):

Or is there any other requirement except the initlized git repo.

no thats the only requirement

I will look into additional logs, something else you could try: rm -rf ~/.local/share/opencode/snapshot

^ that may help?

@rekram1-node commented on GitHub (Oct 12, 2025): > Or is there any other requirement except the initlized git repo. no thats the only requirement I will look into additional logs, something else you could try: `rm -rf ~/.local/share/opencode/snapshot` ^ that may help?
Author
Owner

@SsparKluo commented on GitHub (Oct 13, 2025):

rm -rf ~/.local/share/opencode/snapshot

maybe not? but I found that made a commit may help. If commit first, opencode will work fine (in the host).

And I also try to debug, by clone the opencode and run this test script by bun (all in the docker i found the problem):

import { Snapshot } from './packages/opencode/src/snapshot';
import { Instance } from './packages/opencode/src/project/instance';

(async () => {
  await Instance.provide({
    directory: '/home/node/test',
    fn: async () => {
      const hash = await Snapshot.track();
      console.log('Snapshot hash:', hash);
    }
  });
})();

And the output is

INFO  2025-10-13T04:30:28 +20ms service=project directory=/home/node/test fromDirectory
INFO  2025-10-13T04:30:28 +20ms service=config path=/home/node/.config/opencode/config.json loading
INFO  2025-10-13T04:30:28 +1ms service=config path=/home/node/.config/opencode/opencode.json loading
INFO  2025-10-13T04:30:28 +0ms service=config path=/home/node/.config/opencode/opencode.jsonc loading
INFO  2025-10-13T04:30:28 +27ms service=snapshot initialized
INFO  2025-10-13T04:30:28 +3ms service=snapshot hash= cwd=/home/node/test git=/home/node/.local/share/opencode/snapshot/75098bbddf293b397f61c0c966c3e41ad9e1e3ea tracking
Snapshot hash:

almost the same problem i met at first, only a empty 75098bbddf293b397f61c0c966c3e41ad9e1e3ea folder found.

I remove the .quiet() and .nothrow() in the git init in snapshot/index.ts, and re-run the test script (also remove the snapshot folder), I got this:

32 |     if (await fs.mkdir(git, { recursive: true })) {
33 |       log.info(`GIT_WORK_TREE: ${Instance.worktree}`);
34 |       await $`git init`
                    ^
ShellError: Failed with exit code 128
 exitCode: 128,
   stdout: "",
   stderr: "fatal: Invalid path '/home/node/opencode/--path-format=absolute\n': No such file or directory\n",

      at new ShellPromise (75:16)
      at BunShell (191:35)
      at track (/home/node/opencode/packages/opencode/src/snapshot/index.ts:34:13)

Bun v1.3.0 (Linux x64)

Looks like the path is not sanitized before used.

And if use this

export function sanitizeWorktree(raw: string | undefined | null): string | null {
  if (!raw || typeof raw !== 'string') return null;
  const lines = raw.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
  if (lines.length === 0) return null;
  const abs = lines.find(l => l.startsWith('/') || /^[A-Za-z]:\\/.test(l));
  if (abs) return abs;
  const first = lines[0];
  if (first.startsWith('-')) {
    const candidate = lines.find(l => !l.startsWith('-'));
    return candidate ?? null;
  }
  return first;
} 

to sanitize the Instance.worktree before used, we will get the expect output from the test script.

I haven't check why the worktree doesn't work in the docker, but fine in the host wsl.

@SsparKluo commented on GitHub (Oct 13, 2025): > rm -rf ~/.local/share/opencode/snapshot maybe not? but I found that made a commit may help. If commit first, opencode will work fine (in the host). And I also try to debug, by clone the opencode and run this test script by bun (all in the docker i found the problem): ```ts import { Snapshot } from './packages/opencode/src/snapshot'; import { Instance } from './packages/opencode/src/project/instance'; (async () => { await Instance.provide({ directory: '/home/node/test', fn: async () => { const hash = await Snapshot.track(); console.log('Snapshot hash:', hash); } }); })(); ``` And the output is ``` INFO 2025-10-13T04:30:28 +20ms service=project directory=/home/node/test fromDirectory INFO 2025-10-13T04:30:28 +20ms service=config path=/home/node/.config/opencode/config.json loading INFO 2025-10-13T04:30:28 +1ms service=config path=/home/node/.config/opencode/opencode.json loading INFO 2025-10-13T04:30:28 +0ms service=config path=/home/node/.config/opencode/opencode.jsonc loading INFO 2025-10-13T04:30:28 +27ms service=snapshot initialized INFO 2025-10-13T04:30:28 +3ms service=snapshot hash= cwd=/home/node/test git=/home/node/.local/share/opencode/snapshot/75098bbddf293b397f61c0c966c3e41ad9e1e3ea tracking Snapshot hash: ``` almost the same problem i met at first, only a empty 75098bbddf293b397f61c0c966c3e41ad9e1e3ea folder found. I remove the .quiet() and .nothrow() in the git init in snapshot/index.ts, and re-run the test script (also remove the snapshot folder), I got this: ``` 32 | if (await fs.mkdir(git, { recursive: true })) { 33 | log.info(`GIT_WORK_TREE: ${Instance.worktree}`); 34 | await $`git init` ^ ShellError: Failed with exit code 128 exitCode: 128, stdout: "", stderr: "fatal: Invalid path '/home/node/opencode/--path-format=absolute\n': No such file or directory\n", at new ShellPromise (75:16) at BunShell (191:35) at track (/home/node/opencode/packages/opencode/src/snapshot/index.ts:34:13) Bun v1.3.0 (Linux x64) ``` Looks like the path is not sanitized before used. And if use this ```ts export function sanitizeWorktree(raw: string | undefined | null): string | null { if (!raw || typeof raw !== 'string') return null; const lines = raw.split(/\r?\n/).map(l => l.trim()).filter(Boolean); if (lines.length === 0) return null; const abs = lines.find(l => l.startsWith('/') || /^[A-Za-z]:\\/.test(l)); if (abs) return abs; const first = lines[0]; if (first.startsWith('-')) { const candidate = lines.find(l => !l.startsWith('-')); return candidate ?? null; } return first; } ``` to sanitize the Instance.worktree before used, we will get the expect output from the test script. I haven't check why the worktree doesn't work in the docker, but fine in the host wsl.
Author
Owner

@OpeOginni commented on GitHub (Oct 13, 2025):

The reason why you might be getting this file stderr: "fatal: Invalid path '/home/node/opencode/--path-format=absolute\n': No such file or directory\n", could be that your git commands to get the worktree are failing and for some reason. You are getting the tags used --path-format=absolute.

If you can still update the opencode base, rather than sanitise the worktree returned, try to confirm that the git command actually runs without an error in packages/opencode/src/project/project.ts

    const worktreeResult = await $`git rev-parse --path-format=absolute --show-toplevel`
      .quiet()
      .nothrow()
      .cwd(worktree)
    if (worktreeResult.exitCode !== 0) {
      log.warn("failed to get worktree, using fallback", { exitCode: worktreeResult.exitCode, stdout: worktreeResult.text(), stderr: worktreeResult.stderr.toString() })
      worktree = path.dirname(git)
    } else {
      worktree = worktreeResult.text().trim()
    }

You could do the same thing for getting the hash in packages/opencode/src/snapshot/index.ts

  export async function track() {
...
    const addResult = await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow()
    if (addResult.exitCode !== 0) {
      log.warn("failed to add files to index", { exitCode: addResult.exitCode, stdout: addResult.text(), stderr: addResult.stderr.toString() })
      return ""
    }
    const writeResult = await $`git --git-dir ${git} write-tree`.quiet().cwd(Instance.directory).nothrow()
    if (writeResult.exitCode !== 0) {
      log.warn("failed to write tree", { exitCode: writeResult.exitCode, stdout: writeResult.text(), stderr: writeResult.stderr.toString() })
      return ""
    }
    const hash = writeResult.text().trim()
    log.info("tracking", { hash, cwd: Instance.directory, git })
    return hash
  }

This gives you more logs, that you can share, lets us know why the git commands are failing

@OpeOginni commented on GitHub (Oct 13, 2025): The reason why you might be getting this file ` stderr: "fatal: Invalid path '/home/node/opencode/--path-format=absolute\n': No such file or directory\n",` could be that your git commands to get the worktree are failing and for some reason. You are getting the tags used `--path-format=absolute`. If you can still update the opencode base, rather than sanitise the worktree returned, try to confirm that the git command actually runs without an error in `packages/opencode/src/project/project.ts` ```ts const worktreeResult = await $`git rev-parse --path-format=absolute --show-toplevel` .quiet() .nothrow() .cwd(worktree) if (worktreeResult.exitCode !== 0) { log.warn("failed to get worktree, using fallback", { exitCode: worktreeResult.exitCode, stdout: worktreeResult.text(), stderr: worktreeResult.stderr.toString() }) worktree = path.dirname(git) } else { worktree = worktreeResult.text().trim() } ``` You could do the same thing for getting the hash in `packages/opencode/src/snapshot/index.ts` ```ts export async function track() { ... const addResult = await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() if (addResult.exitCode !== 0) { log.warn("failed to add files to index", { exitCode: addResult.exitCode, stdout: addResult.text(), stderr: addResult.stderr.toString() }) return "" } const writeResult = await $`git --git-dir ${git} write-tree`.quiet().cwd(Instance.directory).nothrow() if (writeResult.exitCode !== 0) { log.warn("failed to write tree", { exitCode: writeResult.exitCode, stdout: writeResult.text(), stderr: writeResult.stderr.toString() }) return "" } const hash = writeResult.text().trim() log.info("tracking", { hash, cwd: Instance.directory, git }) return hash } ``` This gives you more logs, that you can share, lets us know why the git commands are failing
Author
Owner

@SsparKluo commented on GitHub (Oct 13, 2025):

worktree part: no warn, no throw(even remove .nothrow())

hash part: writeResult.exitCode !== 0 true, log.warn("failed to write tree", { exitCode: writeResult.exitCode, stdout: writeResult.text(), stderr: writeResult.stderr.toString() }) ran, log has WARN 2025-10-13T15:01:49 +2ms service=snapshot exitCode=128 stdout= stderr=fatal: not a git repository: '/home/node/.local/share/opencode/snapshot/75098bbddf293b397f61c0c966c3e41ad9e1e3ea' failed to add files to index

I think the problem is the git rev-parse --path-format=absolute --show-toplevel return

--path-format=absolute
/home/node/opencode

but it should return only /home/node/opencode (this is what I got from the git rev-parse --path-format=absolute --show-toplevel in the host wsl debian12). The git version in the host debian12 is git version 2.39.5 and in the container (debian11) is git version 2.30.2.

UPDATE: The git version is the key, git introduced rev-parse's path-format option in git 2.31.0

@SsparKluo commented on GitHub (Oct 13, 2025): worktree part: no warn, no throw(even remove `.nothrow()`) hash part: `writeResult.exitCode !== 0` true, `log.warn("failed to write tree", { exitCode: writeResult.exitCode, stdout: writeResult.text(), stderr: writeResult.stderr.toString() })` ran, log has `WARN 2025-10-13T15:01:49 +2ms service=snapshot exitCode=128 stdout= stderr=fatal: not a git repository: '/home/node/.local/share/opencode/snapshot/75098bbddf293b397f61c0c966c3e41ad9e1e3ea' failed to add files to index` I think the problem is the `git rev-parse --path-format=absolute --show-toplevel` return ``` --path-format=absolute /home/node/opencode ``` but it should return only `/home/node/opencode` (this is what I got from the `git rev-parse --path-format=absolute --show-toplevel` in the host wsl debian12). The git version in the host debian12 is `git version 2.39.5` and in the container (debian11) is `git version 2.30.2`. UPDATE: The git version is the key, git introduced rev-parse's path-format option in git 2.31.0
Author
Owner

@OpeOginni commented on GitHub (Oct 13, 2025):

That's massive, did bumping the version solve it?

@OpeOginni commented on GitHub (Oct 13, 2025): That's massive, did bumping the version solve it?
Author
Owner

@SsparKluo commented on GitHub (Oct 13, 2025):

That's massive, did bumping the version solve it?

Yes, solved after updating the git to 2.39

@SsparKluo commented on GitHub (Oct 13, 2025): > That's massive, did bumping the version solve it? Yes, solved after updating the git to 2.39
Author
Owner

@OpeOginni commented on GitHub (Oct 13, 2025):

Yes, solved after updating the git to 2.39

Noice, so if it's all good I think you can close the issue 👌

@OpeOginni commented on GitHub (Oct 13, 2025): > Yes, solved after updating the git to 2.39 Noice, so if it's all good I think you can close the issue 👌
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#2031