windmill-labs/windmill · error
file only applies to multi-file apps; ${type} "${path}" diff
Error message
file only applies to multi-file apps; ${type} "${path}" diffs as a single document — call again without file. What it means
The `file` argument of the copilot diff tool narrows a diff to one file of a multi-file item (apps). When the computed diff produced no `files` map — the item is a single-document kind — passing `file` is invalid and the tool throws this corrective error telling the agent to retry without it. It is an argument-surface misuse guard, not a data failure.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:6657
}
}
const changedFileCount = files ? Object.keys(files).length : 0
if (!patch && changedFileCount === 0) {
// A secret's sides are masked on both ends — an empty patch cannot prove
// the value is unchanged.
const message = valueUncomparable
? `No visible changes for ${type} "${path}" — but a secret's value cannot be compared and may have been updated in the draft.`
: `Draft matches the deployed version of ${type} "${path}" — no changes.`
toolCallbacks.setToolStatus(toolId, { content: message, result: message })
return flushCaveat + message
}
const header = noDeployed
? `${type} "${path}" has no deployed version yet — the entire draft is new.\n\n`
: `Draft changes vs deployed for ${type} "${path}":\n\n`
if (args.file !== undefined && !files) {
throw new Error(
`file only applies to multi-file apps; ${type} "${path}" diffs as a single document — call again without file.`
)
}
const body = files
? renderEntryFiles(files, patch, args)
: windowPatchBody(patch, args.offset ?? 0, args.limit ?? DIFF_READ_DEFAULT_LINES)
const result = flushCaveat + header + body
toolCallbacks.setToolStatus(toolId, {
content: `Draft vs deployed diff for "${path}"`,
result
})
return result
}
// Body of an item read for a multi-file app: one file's patch when `file` is
// given, otherwise the per-file summary plus config changes.
function renderEntryFiles(
files: Record<string, DiffFileView>,View on GitHub (pinned to e474e8803c)
Solutions
- Retry the same diff call without the `file` argument
- If you need per-file output for a multi-file app, confirm the type is 'app' or 'raw_app' before passing `file`
Example fix
// before
diff({ type: 'script', path: 'f/build', file: 'main.ts' })
// after
diff({ type: 'script', path: 'f/build' }) Defensive patterns
Strategy: validation
Validate before calling
if (args.file !== undefined && !['app', 'raw_app'].includes(args.type)) {
throw new Error('file is only valid for app diffs')
} Type guard
function supportsFileFilter(args: { type: string; file?: string }): boolean {
return args.file === undefined || args.type === 'app' || args.type === 'raw_app'
} Try / catch
try {
return await diffItem(args)
} catch (e) {
if (String(e?.message).startsWith('file only applies to multi-file apps')) {
return await diffItem({ ...args, file: undefined })
}
throw e
} Prevention
- Only pass `file` when the type is 'app' or 'raw_app'
- Drop the file argument when retrying after this error
- Remember single-document kinds (script, flow, variable) never have a files map
When it happens
Trigger: Calling diff with `args.file` set while the item is a single-document type (script, flow, variable, resource, etc.) or the diff entry has no files map, i.e. `args.file !== undefined && !files`.
Common situations: An agent habitually includes `file` from a previous app-diff call; a template call copied from an app example applied to a script; confusing multi-file apps with single-file scripts.
Related errors
- Missing required arguments: ${required.join(", ")}. Use -d '
- No changes in file "${requested}". Changed files: ${changed}
- Workspace "${workspace}" is not a fork — it has no parent wo
- path is required.
- file only applies to multi-file apps; ${entry.kind} "${path}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/89955ac1d61b0b47.
Report an issue: GitHub.