Skip to content

Conversation

@phil535
Copy link

@phil535 phil535 commented Nov 29, 2025

This bugfix relates to Issue #1436.

Note: This issue was analyzed and implemented using Claude Code. The solution is coherent to me but I do not have insight on the project.

This implementation is not tested!

Problem

SFTP files (and other cached remote files) sometimes display mixed content from different files.
Specifically:

  • The beginning of the file shows the correct content
  • Trailing portions contain random content from previously cached files
  • Most commonly observed with files sharing the same name in different directories (e.g. vars/main.yml)

Actual content:
actual_content

Acode content:
acode_content

Root Cause

The internalFs.writeFile() function in src/fileSystem/internalFs.js uses the Cordova FileWriter API without truncating the file before writing. When writing new content that is shorter than the existing cached file, the old trailing data remains in the file.

Example:

  1. Cache file contains: "Long content from project1/vars/main.yml with lots of data..."
  2. User opens: project2/vars/main.yml (shorter file)
  3. SFTP downloads: "Short YAML"
  4. Result: "Short YAMLt from project1/vars/main.yml with lots of data..."

Performance Impact

  • Adds one additional async operation (truncate)
  • Modern filesystems optimize this efficiently (metadata update + block reuse)
  • Negligible performance impact compared to correctness gain

Affected Areas

  • SFTP file caching (most common)
  • FTP file caching
  • Any operation using internalFs.writeFile() with existing files

@phil535 phil535 marked this pull request as ready for review November 29, 2025 06:43
Copy link
Member

@bajrangCoder bajrangCoder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

writeFile now truncates then writes, which fixes stale trailing bytes. However, writer.onwriteend triggers after truncate(0) even when an error occurs, the handler immediately writes new data, causing a second write after a failed truncate and leaving the promise already rejected.

@RohitKushvaha01
Copy link
Member

RohitKushvaha01 commented Jan 4, 2026

This might work but I can't test it right now

fileEntry.createWriter((writer) => {
    writer.onerror = (e) => reject(e.target.error);


    writer.onwrite = () => {
        
        writer.onwrite = () => resolve(filename);
        writer.write(data);
    };

    writer.truncate(0);
});

@phil535 phil535 force-pushed the fix/file-write-truncation-bug branch from 104f0fc to f22eb85 Compare January 4, 2026 06:06
@phil535
Copy link
Author

phil535 commented Jan 4, 2026

This might work but I can't test it right now

fileEntry.createWriter((writer) => {
    writer.onerror = (e) => reject(e.target.error);


    writer.onwrite = () => {
        
        writer.onwrite = () => resolve(filename);
        writer.write(data);
    };

    writer.truncate(0);
});

Thanks for your input.

Unfortunately i cannot test it either, but I changed the PR accordingly.

@RohitKushvaha01 RohitKushvaha01 added the CI: RUN ON-DEMAND PREVIEW RELEASES Triggers an on-demand preview build for this pull request via CI workflow. label Jan 4, 2026
@github-actions github-actions bot removed the CI: RUN ON-DEMAND PREVIEW RELEASES Triggers an on-demand preview build for this pull request via CI workflow. label Jan 4, 2026
@github-actions

This comment has been minimized.

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Preview Release for this, has been built.

Click here to view that github actions build

@bajrangCoder

This comment was marked as resolved.

@greptile-apps
Copy link

greptile-apps bot commented Jan 6, 2026

Greptile Summary

This PR attempts to fix a critical file corruption bug where cached remote files (SFTP/FTP) display mixed content from different files when new content is shorter than old cached content.

Changes Made

  • Added writer.truncate(0) to clear existing file content before writing new data
  • Restructured callback flow to truncate before writing

Critical Issue Found

The implementation uses the non-standard onwrite event instead of the correct onwriteend event. The Cordova FileWriter API (based on W3C File API) only supports onwriteend, onwrite, onabort, onprogress, and onerror - but onwrite is not a valid event name for completion callbacks. This means:

  • The truncate operation completes but fires onwriteend (not onwrite)
  • The registered onwrite callbacks never execute
  • The write operation never happens
  • The Promise never resolves, causing the operation to hang

Root Cause of Original Bug

The diagnosis is correct - without truncation, writing shorter content to an existing file leaves trailing garbage data from the previous file content.

Fix Required

Change writer.onwrite to writer.onwriteend on lines 53-54 to use the correct FileWriter API event.

Confidence Score: 0/5

  • This PR contains a critical bug that will cause all file write operations to fail
  • While the approach (truncating before writing) is correct and addresses the root cause described in issue Caching Causing File Mixing #1436, the implementation uses a non-existent API event (onwrite instead of onwriteend). This will cause all write operations using internalFs.writeFile() to hang indefinitely, breaking SFTP/FTP file saving, settings persistence, and any other functionality that writes files. The PR cannot be merged without fixing this critical API misuse.
  • src/fileSystem/internalFs.js requires immediate correction of the event handler names

Important Files Changed

Filename Overview
src/fileSystem/internalFs.js Critical bug: uses non-standard onwrite event instead of onwriteend, causing write operations to fail silently

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant SFTP as SFTP/FTP Module
    participant IFS as internalFs.writeFile
    participant CFS as Cordova FileSystem
    participant Writer as FileWriter

    App->>SFTP: Request to save remote file
    SFTP->>IFS: writeFile(localCache, content, true, false)
    IFS->>CFS: resolveLocalFileSystemURL(dirname)
    CFS-->>IFS: Directory entry
    IFS->>CFS: getFile(name, {create, exclusive})
    CFS-->>IFS: File entry
    IFS->>Writer: createWriter()
    Writer-->>IFS: Writer instance
    IFS->>Writer: Set onerror handler
    IFS->>Writer: Set onwrite handler (BUG: should be onwriteend)
    IFS->>Writer: truncate(0)
    Note over Writer: truncate completes, fires onwriteend
    Note over Writer: onwrite never fires - wrong event!
    Note over IFS: Callbacks never execute
    Note over IFS: File write never happens
    Note over IFS: Promise never resolves
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments (1)

  1. src/fileSystem/internalFs.js, line 53-54 (link)

    logic: onwrite is not a standard FileWriter API event. The correct event is onwriteend. This will cause the truncate and write operations to fail silently since the callbacks never fire.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

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.

3 participants