Help: File Out DAT ignores subfolders and saves to project root only in TouchPlayer

Hi everyone,

I’m running into an issue where my File Out DAT saves perfectly into a subfolder when running in TouchDesigner, but when I open the exact same .toe file in TouchPlayer, it completely ignores the subfolder and dumps the files into the root of the project directory.

I’ve built a minimal toy example to debug this and have done some deep investigation, but I’m completely stuck on why TouchPlayer is rejecting valid absolute paths.

The Setup (Toy Example)

  • I have a Script DAT with a custom parameter for a subfolder name (e.g., children) and an increment counter for file name.

  • This feeds into a File Out DAT with the following expression in the File parameter: project.folder + '/' + op('script1').par.Path + '/' + str(op('script1').par.Increment) + '.txt'

What I’ve Tried:

  1. Relative Paths: Initially, I used a standard relative path (children/1.txt). In TouchDesigner it works, but in TouchPlayer it saves to the project root.
  2. Absolute Paths: I explicitly switched to using project.folder to enforce an absolute path. Even though the expression evaluates to a perfectly valid absolute path (e.g., C:/Users/Name/Desktop/test/children/1.txt), TouchPlayer still strips the children subfolder and saves it as C:/Users/Name/Desktop/test/1.txt.
  3. Parameter Types: I changed the custom parameter type from Folder to String just in case TouchPlayer was incorrectly validating the Folder parameter on startup. It still saves to the project root.

Both 1 and 2 has been done using an existing folder.

The only workaround so far has been using the following script

import os

def onPulse(par):

if par.name == 'Save':

		folder_name = par.owner.par.Path.eval()

		increment = par.owner.par.Increment.eval()

# Build absolute directory path using project.folder

		abs_dir = os.path.join(project.folder, folder_name)

# Ensure the folder actually exists!

		os.makedirs(abs_dir, exist_ok=True)

		file_path = os.path.join(abs_dir, f"{increment}.txt")

		target_dat = op('out1') # The DAT containing the text

if target_dat:

# Append using native Python

with open(file_path, 'a') as f:

				f.write(target_dat.text)

return

Yet, I need to use this in a project that saves long txt files (10k rows approx in total) in chunks and I would rather prefer to rely on native operators instead of Python to ensure they can keep up with writing.

I’ve attached my toy_example.zip to reproduce the issue. Any insights would be hugely appreciated, since I’m stuck in the investigation!

toy_example.zip (73.3 KB)

I’m on Windows 11 and using TD version 2025.31760

Hi @Nicorb932

testing this by skipping over the Script DAT’s onPulse callback and ran that in version 2025.32820 TouchPlayer - the file is written to the subfolder called “children“ as expected.

Is the original failing file also using a Script DAT? It’s a curious setup, hence me asking.

cheers
Markus

1 Like

The original project is using the following setup (I attach a screenshot even if I assume is not completely understandable)

Briefly, I regularly pulse the “write” parameter of “trail_writer” DAT using the “saver_clock” Timer CHOP.

I tested the toy example as you suggested: I removed the Script DAT and exposed the parameter panel of the File Out DAT in the perform window UI to test it in TouchPlayer.

I’ve found that using a commercial license it works properly, but it doesn’t with when test on a machine with non-commercial license. I tested twice on the same machine with both licenses.

Is this expected?

Hi @Nicorb932

that is unexpected. Could you share the example that you tested with for me to try to replicate?

cheers
Markus

1 Like

I leave a link to a google drive folder containing two videos demonstrating the bug with the two licenses (same machine and same TouchPlayer version) and the .toe file used.

The toe is basically the same as the toy example attached in my first message but without the Script DAT.

Thank you very much for the availability,

best, Nicola

Hi @Nicorb932

thanks for sending this and I can replicate with a Non-Commercial license.
We are having a look.

cheers
Markus