Skip to content

FileSystem.FileHandle.writeFromOffset ignores its offset and writes at position 0 #63

Description

@gilramir

Found against:* gren 0.6.6, gren-lang/core 7.4.2, gren-lang/node 6.1.3, node 22
Reproduction: in https://github.com/gilramir/gren-bug-reports in 2026-09-11-writefromoffset; ./run.sh prints the figure below.

Reproduction

Write ten As, open the file for writing, put two Zs at offset 5, read it
back:

FileSystem.writeFile fsPermission (Bytes.fromString "AAAAAAAAAA") path
    |> Task.andThen
        (\_ -> FileHandle.openForWrite fsPermission FileHandle.ExpectExisting path)
    |> Task.andThen
        (\fh ->
            FileHandle.writeFromOffset fh 5 (Bytes.fromString "ZZ")
                |> Task.andThen (\_ -> FileHandle.close fh)
        )
    |> Task.andThen (\_ -> FileSystem.readFile fsPermission path)
wrote "ZZ" at offset 5 into "AAAAAAAAAA"
  expected  AAAAAZZAAA
  got       ZZAAAAAAAA

The "ZZ"'s were written at offset 0.

Reason

writeFromOffset takes the offset as a plain Int:

{-| Write bytes into a specific location of a file.
-}
writeFromOffset : WriteableFileHandle a -> Int -> Bytes -> Task FileSystem.Error (WriteableFileHandle a)
writeFromOffset =                                                     
    Gren.Kernel.FileSystem.writeFromOffset

The kernel function behind it reads that argument as a record:

var _FileSystem_writeFromOffset = F3(function (fh, options, bytes) {
  return __Scheduler_binding(function (callback) {               
    _FileSystem_writeHelper(
      fh,
      bytes,
      0,                                
      bytes.byteLength,
      options.__$offset,
      callback,
    );
  }); 
});

options is a number, so options.__$offset is undefined. That is handed to
fs.write as its position, where undefined means "wherever the file
currently is" — which for a freshly opened handle is 0.

So the offset argument has no effect at all. Every writeFromOffset writes
where write would have written.

readFromOffset beside it does take a record, { offset, length }, and is
correct. The kernel function for the write looks like a copy of the read's that
kept the record access after the Gren signature stopped passing one.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions