windmill-labs/windmill · error
Content of raw app file ${filePath} is not a string
Error message
Content of raw app file ${filePath} is not a string What it means
When reading a raw app's files from a zip archive during sync, each entry lazily returns its text via getContentText(). The CLI throws this error if the stored content value for a file is not a string, meaning the raw app's `value.files` (or equivalent zip data) holds a non-string (object, number, null) where file text was expected.
Source
Thrown at cli/src/commands/sync/sync.ts:1470
filePath === "/DATATABLES.md"
) {
continue;
}
// Strip only a leading `/` (keys are app-root-relative), so the
// relative path handed to the guard matches what the backend's
// `strip_prefix('/')` validates — the two must not disagree on a
// non-`/` key, or a deploy the backend allows would abort the pull.
const filePathInApp = rawAppPathWithinFolder(
finalPath,
filePath.replace(/^\//, ""),
);
yield {
isDirectory: false,
path: filePathInApp,
async *getChildren() {},
async getContentText() {
if (typeof content !== "string") {
throw new Error(
`Content of raw app file ${filePath} is not a string`,
);
}
return content as string;
},
};
}
} catch (error) {
log.error(`Failed to extract files for raw app at path: ${p}`);
throw error;
}
// Yield inline script content and lock files
for (const s of inlineScripts) {
yield {
isDirectory: false,
path: path.join(finalPath, APP_BACKEND_FOLDER, s.path),
async *getChildren() {},View on GitHub (pinned to e474e8803c)
Solutions
- Open the raw app definition and make every `value.files` entry a plain string of file text (encode binaries as needed, e.g. base64 with the convention your app uses)
- Check CLI/instance version compatibility and upgrade the CLI to match the export format
- Re-export the raw app from the instance rather than hand-editing the JSON
- If calling this API from code, validate `typeof content === 'string'` before constructing the entry
Example fix
// before
"files": { "index.js": { "content": "console.log(1)" } }
// after
"files": { "index.js": "console.log(1)" } Defensive patterns
Strategy: type-guard
Validate before calling
const files = rawApp.value.files ?? {};
const bad = Object.entries(files).filter(([, v]) => typeof v !== 'string');
if (bad.length) throw new Error(`non-string file content: ${bad.map(([k]) => k).join(', ')}`); Type guard
function isStringFileMap(v: unknown): v is Record<string, string> {
return typeof v === 'object' && v !== null &&
Object.values(v).every(x => typeof x === 'string');
} Try / catch
try {
const text = await entry.getContentText();
} catch (e) {
if (String(e).includes('is not a string')) {
// inspect the app definition's value.files entry for this path
} else throw e;
} Prevention
- Keep value.files values as plain strings of file text
- Re-export apps from the instance instead of hand-editing exported JSON
- Match CLI version to the instance version before syncing raw apps
When it happens
Trigger: Pushing/pulling a raw app whose `value.files` map contains a non-string value for some file key — e.g. someone pasted an object instead of file text, a tool serialized content as {content: ...}, or an older/newer export format is being read by an incompatible CLI version.
Common situations: Hand-editing or script-generating a raw app JSON with wrong shapes; version mismatch between the instance that exported the app and the CLI reading it; a migration converting binary/encoded file content into objects.
Related errors
- raw app path ${JSON.stringify(relPath)} escapes the app fold
- Cannot get content of directory
- Cannot get content of folder
- result.substring(__RESULT_ERR_PREFIX.length)
- Workspace folder not found, are you in the right directory?
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/a0a0dcb84e473141.
Report an issue: GitHub.