Skip to content

fix(uploads): stage outgoing documents in the OS temp directory - #287

Open
Realmagnum wants to merge 1 commit into
grinev:mainfrom
Realmagnum:fix/document-upload-temp-dir
Open

Realmagnum wants to merge 1 commit into
grinev:mainfrom
Realmagnum:fix/document-upload-temp-dir

Conversation

@Realmagnum

Copy link
Copy Markdown
Contributor

What

TelegramEventDelivery.sendDocument() now stages the outgoing document under os.tmpdir()/opencode-telegram-bot instead of <installed package>/dist/.tmp.

Why

The scratch directory was resolved relative to the compiled module:

const TEMP_DIR = path.join(__dirname, "..", "..", ".tmp");

For a global install (npm i -g) the package directory is owned by root and is not writable by the user the bot runs as, so fs.mkdir(TEMP_DIR) failed with EACCES and every tool-output file was dropped:

[ToolBatcher] Failed to send tool file message: ... reason=enqueue_file Error: EACCES: permission denied,
mkdir '/usr/lib/node_modules/@grinev/opencode-telegram-bot/dist/.tmp'

os.tmpdir() is writable for the process and honours TMPDIR, so uploads no longer depend on installation layout or ownership (global install, Docker, read-only package dir).

Fixes #286.

Changes

  • src/bot/events/telegram-event-delivery.ts — use os.tmpdir(); drop the now-unused fileURLToPath/__dirname scaffolding.
  • tests/bot/events/telegram-event-delivery.test.ts — new test asserting the staged path lives under the OS temp directory, not beside the package.

Testing

  • npx vitest run tests/bot/events/telegram-event-delivery.test.ts — 6 passed
  • npm test — 202 files, 2807 passed, 7 skipped
  • npm run lint, npm run typecheck — clean
  • Manual check against the built dist/: the InputFile handed to sendDocument pointed at /tmp/opencode-telegram-bot/write_x.txt, the file existed with the expected content while sending, and was removed afterwards.

sendDocument() wrote the upload scratch file to dist/.tmp inside the
installed package. A global npm install is owned by root, so a bot
running as an unprivileged user failed with EACCES on mkdir and every
tool-output file upload was dropped.

Use os.tmpdir() instead, which is writable for the process and honours
TMPDIR.
@grinev

grinev commented Oct 27, 2026

Copy link
Copy Markdown
Owner

@Realmagnum thanks for the fix! You found the root cause correctly: dist/.tmp is not writable in a global install.

Required: /tmp/opencode-telegram-bot is a fixed path in a shared /tmp on Linux, and the file names are predictable (write_<basename>.txt). Another local user can create this directory first. Then uploads fail with EACCES again, or a symlink inside it makes fs.writeFile overwrite a file of the bot user (for example ~/.bashrc).

We don't need a temp file at all, because grammY accepts a buffer directly:

await destination.api.sendDocument(
  destination.chatId,
  new InputFile(fileData.buffer, fileData.filename),
  { caption: fileData.caption, ...options },
);

This lets us remove TEMP_DIR, mkdir/writeFile/unlink and the unused imports. It also fixes collisions when two files with the same name are sent at the same time. Please update the test to check that sendDocument gets an InputFile with the right filename.

Optional: the .tmp/ entry in .gitignore can be removed after this change.

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.

[Bug]: Tool-output file uploads fail with EACCES because scratch files are written into the installed package (dist/.tmp)

3 participants