Skip to content

feat: Add PDF Upload endpoint to EntryResource#14963

Merged
koppor merged 2 commits intoJabRef:mainfrom
sonthien22501:feat/pdf-upload-clean
Feb 5, 2026
Merged

feat: Add PDF Upload endpoint to EntryResource#14963
koppor merged 2 commits intoJabRef:mainfrom
sonthien22501:feat/pdf-upload-clean

Conversation

@sonthien22501
Copy link
Copy Markdown
Contributor

Closes Research_Cockpit#91

Summary

This PR implements the missing POST /libraries/{id}/entries/{entryId}/files endpoint in the HTTP API. This allows external tools (like browser extensions) to upload and attach PDF files to existing bibliographic entries.

Changes

  • Modified EntryResource.java: Added the addFile method to handle multipart/form-data or raw PDF streams.
  • Logic:
    • Saves the uploaded stream to a temporary file. As I research, while sending pdf to local machine, it sends as Stream of Bytes and LinkedFile works by finding at an existing file.
    • Uses LinkedFileHandler.renameToSuggestedName() to automatically rename the file according to user preferences (e.g., Author - Title.pdf) and handle collisions.
    • Moves the file to the library's configured file directory (or throws BadRequestException if not configured).
    • Links the file to the specified BibEntry.

Steps to test

  1. Start JabRef with the HTTP Server enabled (Preferences > Network > Allow remote operation).
  2. Open a Library and ensure it has at least one entry. Note its Citation Key (e.g., Test2026).
  3. Run Curl Command: Upload a PDF file to that entry.
    curl -v -X POST -H "Content-Type: application/pdf" --data-binary @path/to/your/test.pdf http://localhost:23119/libraries/current/entries/Test2026/files
  4. Verify: Check the entry in JabRef. It should now have the PDF attached, and the file should be renamed correctly on disk.
Screenshot 2026-01-30 at 14 26 12

Mandatory checks

@jabref-machine
Copy link
Copy Markdown
Collaborator

Note that your PR will not be reviewed/accepted until you have gone through the mandatory checks in the description and marked each of them them exactly in the format of [x] (done), [ ] (not done yet) or [/] (not applicable). Please adhere to our pull request template.

@github-actions github-actions bot added the status: changes-required Pull requests that are not yet complete label Jan 30, 2026
Copy link
Copy Markdown
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

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

Minor comments in line.

Please add entry to CHANGELOG.md

// 2. Save stream to temporary file
// We must save to a temp file first because LinkedFileHandler requires an existing Path to generate the suggested filename based on content/metadata.
// We use the target directory to ensure we are on the same file system.
java.nio.file.Path tempFile = Files.createTempFile(targetDir, "jabref-upload", ".pdf");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Plesae re-add the timestamp here again - as suffix after -upload. Use ISO 8601 here - example: 20201209T160953Z . No need for time hones.

See also https://stackoverflow.com/a/65221179/873282

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

        // 3. Save stream to temporary file
        String timestamp = ZonedDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
        java.nio.file.Path tempFile = Files.createTempFile(targetDir, "jabref-upload-" + timestamp + "-", ".pdf");
        Files.copy(fileInputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);

Should I change to this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice, thanks!

@POST
@Path("files")
@Consumes("application/pdf")
public void addFile(@PathParam("id") String id, @PathParam("entryId") String entryId, InputStream fileInputStream) throws IOException {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

JAX-RS Response instead of void.

Files.copy(fileInputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);

// 3. Create LinkedFile
LinkedFile linkedFile = new LinkedFile("", tempFile, "PDF");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Java offers strong typing.

Use

Suggested change
LinkedFile linkedFile = new LinkedFile("", tempFile, "PDF");
LinkedFile linkedFile = new LinkedFile("", tempFile, StandardFileType.PDF);


// 3. Create LinkedFile
LinkedFile linkedFile = new LinkedFile("", tempFile, "PDF");
LinkedFileHandler fileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, preferences.getFilePreferences());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Move this after 4., because it belongs to there - and not to step 3.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

       // 4. Create LinkedFile
        LinkedFile linkedFile = new LinkedFile(tempFile.getFileName().toString(), tempFile, "PDF");

        // 5. Rename to suggested pattern (e.g. Author - Title.pdf)
        LinkedFileHandler fileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, preferences.getFilePreferences());

// 4. Rename to suggested pattern (e.g. Author - Title.pdf)
boolean renameSuccessful = fileHandler.renameToSuggestedName();
if (!renameSuccessful) {
throw new IOException("Failed to rename file to suggested pattern");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remember you are in a HTTP Server context -- You can create better replies here... If you use JAX-RS response (see https://github.com/JabRef/jabref/pull/14963/changes#r2753557164), you can do.

- Change return type to Response (200/204)
- Use ISO 8601 timestamp for temp files
- Use LinkedFileHandler.renameToSuggestedName() for logic reuse
- Use StandardFileType constant
- Remove fallback directory logic (fail fast)
@sonthien22501
Copy link
Copy Markdown
Contributor Author

I have updated the code in the new commit. Please review it again.
Thank you for your time!

@koppor
Copy link
Copy Markdown
Member

koppor commented Feb 5, 2026

CHANGELOG.md can be skipped this time as this is not that imporant. In furute, also add a CHANGELOG.md entry

@koppor koppor marked this pull request as ready for review February 5, 2026 13:11
@koppor koppor enabled auto-merge February 5, 2026 13:11
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add PDF file upload endpoint to EntryResource

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implements POST endpoint for uploading PDF files to entries
• Saves uploaded stream to temporary file with ISO 8601 timestamp
• Renames file using LinkedFileHandler for consistent naming patterns
• Links file to BibEntry and returns appropriate HTTP response
Diagram
flowchart LR
  A["PDF Upload Request"] --> B["Validate Entry Exists"]
  B --> C["Get Target Directory"]
  C --> D["Save to Temp File"]
  D --> E["Create LinkedFile"]
  E --> F["Rename to Suggested Pattern"]
  F --> G["Add to BibEntry"]
  G --> H["Return Response"]
Loading

Grey Divider

File Changes

1. jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java ✨ Enhancement +59/-0

Add PDF upload endpoint with file handling logic

• Added addFile() method to handle POST requests for PDF file uploads
• Imports new dependencies for file handling, streaming, and HTTP responses
• Validates entry existence and target directory configuration
• Creates temporary file with ISO 8601 timestamp prefix
• Uses LinkedFileHandler.renameToSuggestedName() to rename files according to user preferences
• Returns 204 No Content on successful rename or 200 OK with message if rename fails

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Missing id/entryId validation 📘 Rule violation ⛨ Security
Description
• The new addFile endpoint uses id and entryId directly without validating null/blank/invalid
  values.
• This allows malformed requests (e.g., empty citation keys) to reach lookup and file-handling
  logic, producing unclear failures and increasing the risk of unexpected behavior.
• Add explicit request validation and fail fast with a clear BadRequestException before
  processing.
Code

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[R142-145]

+    public Response addFile(@PathParam("id") String id, @PathParam("entryId") String entryId, InputStream fileInputStream) throws IOException {
+        BibDatabaseContext databaseContext = getDatabaseContext(id);
+        List<BibEntry> entriesByCitationKey = databaseContext.getDatabase().getEntriesByCitationKey(entryId);
+        if (entriesByCitationKey.isEmpty()) {
Evidence
Compliance requires validating external inputs before use. In addFile, the external path
parameters are immediately used to fetch a database context and perform an entry lookup, with no
null/blank checks or other validation on id/entryId (only an existence check after lookup).

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[142-145]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `POST .../files` endpoint processes external inputs (`id`, `entryId`, and the upload body) without validating that they are present and non-blank.

## Issue Context
This endpoint is reachable via HTTP. Validation should happen before calling `getDatabaseContext(id)` and before using `entryId` to query the database.

## Fix Focus Areas
- jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[142-158]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Temp file not cleaned 📘 Rule violation ⛯ Reliability
Description
• The upload stream is copied to a temp file, but there is no try/finally cleanup if subsequent
  steps fail, which can leave orphaned files in the library file directory.
• The method also does not explicitly manage the lifetime of the provided InputStream, making
  diagnosis and resource handling less robust.
• Wrap file IO in a minimal try block and ensure temp files are deleted on failure (and consider
  try-with-resources for the stream).
Code

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[R160-176]

+        // 3. Save stream to temporary file
+        String timestamp = ZonedDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
+        java.nio.file.Path tempFile = Files.createTempFile(targetDir, "jabref-upload-" + timestamp + "-", ".pdf");
+        Files.copy(fileInputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
+
+        // 4. Create LinkedFile
+        LinkedFile linkedFile = new LinkedFile(tempFile.getFileName().toString(), tempFile, "PDF");
+
+        // 5. Rename to suggested pattern (e.g. Author - Title.pdf)
+        LinkedFileHandler fileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, preferences.getFilePreferences());
+        boolean renameSuccessful = fileHandler.renameToSuggestedName();
+        if (!renameSuccessful) {
+            LOGGER.warn("Renaming to suggested name failed. Keeping temp filename.");
+        }
+
+        // 6. Add to entry
+        entry.addFile(linkedFile);
Evidence
The compliance checklist requires handling failure points and edge cases. The code creates and
writes a temp file, then proceeds with rename and entry modification without any cleanup path if an
exception occurs after the copy, which can leak files and cause accumulation over time.

Rule 3: Generic: Robust Error Handling and Edge Case Management
jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[160-176]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The endpoint writes uploaded data to a temp file but does not guarantee cleanup if later operations fail, potentially leaving orphaned files in the library directory.

## Issue Context
`Files.createTempFile(...)` is executed inside the library file directory. Any exception thrown after creation/copy (or a failure path) can leave behind files.

## Fix Focus Areas
- jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[160-183]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Upload not persisted 🐞 Bug ✓ Correctness
Description
• In headless/server mode, the library is imported from disk per request, but addFile only mutates
  the in-memory BibEntry and never writes the updated database back to the .bib file, so the
  attachment is effectively lost.
• In GUI mode, other write endpoints route changes through UiMessageHandler/UiCommand and restrict
  to "current"; this endpoint directly mutates the model, risking thread-safety and inconsistent
  update behavior.
Code

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[R142-177]

+    public Response addFile(@PathParam("id") String id, @PathParam("entryId") String entryId, InputStream fileInputStream) throws IOException {
+        BibDatabaseContext databaseContext = getDatabaseContext(id);
+        List<BibEntry> entriesByCitationKey = databaseContext.getDatabase().getEntriesByCitationKey(entryId);
+        if (entriesByCitationKey.isEmpty()) {
+            throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id);
+        }
+
+        // 1. Determine BibEntry
+        BibEntry entry = entriesByCitationKey.getFirst();
+
+        // 2. Determine target directory
+        Optional<java.nio.file.Path> targetDirOpt = databaseContext.getFirstExistingFileDir(preferences.getFilePreferences());
+
+        if (targetDirOpt.isEmpty()) {
+            throw new BadRequestException("Library must be saved or have a file directory configured to attach files.");
+        }
+        java.nio.file.Path targetDir = targetDirOpt.get();
+
+        // 3. Save stream to temporary file
+        String timestamp = ZonedDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
+        java.nio.file.Path tempFile = Files.createTempFile(targetDir, "jabref-upload-" + timestamp + "-", ".pdf");
+        Files.copy(fileInputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
+
+        // 4. Create LinkedFile
+        LinkedFile linkedFile = new LinkedFile(tempFile.getFileName().toString(), tempFile, "PDF");
+
+        // 5. Rename to suggested pattern (e.g. Author - Title.pdf)
+        LinkedFileHandler fileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, preferences.getFilePreferences());
+        boolean renameSuccessful = fileHandler.renameToSuggestedName();
+        if (!renameSuccessful) {
+            LOGGER.warn("Renaming to suggested name failed. Keeping temp filename.");
+        }
+
+        // 6. Add to entry
+        entry.addFile(linkedFile);
+
Evidence
EntryResource.addFile mutates the entry (entry.addFile) after loading a database context. In
headless mode, ServerUtils creates a fresh BibDatabaseContext by importing the library file;
JabRefSrvStateManager indicates there are no open databases to mutate/persist. Other mutating
endpoints in jabsrv enforce GUI-mode and use UiMessageHandler/UiCommand instead of direct model
mutation.

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[142-182]
jabsrv/src/main/java/org/jabref/http/server/services/ServerUtils.java[57-90]
jabsrv/src/main/java/org/jabref/http/JabRefSrvStateManager.java[16-41]
jabsrv/src/main/java/org/jabref/http/server/resources/EntriesResource.java[53-88]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`EntryResource.addFile` attaches a file to an entry but does not persist that change in headless/server mode (the DB context is freshly imported from disk). In GUI mode it bypasses the established mutation path (UiMessageHandler/UiCommand + &quot;current&quot; restriction), which can lead to thread-safety and consistency issues.

### Issue Context
- In headless mode (`srvStateManager instanceof JabRefSrvStateManager`), `ServerUtils.getBibDatabaseContext` imports the library file into a new `BibDatabaseContext` and returns it. Mutations to that context are not automatically saved.
- Other write endpoints in `jabsrv` explicitly require GUI mode and dispatch changes via `uiMessageHandler.handleUiCommands(...)`.

### Fix Focus Areas
- jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[139-183]
- jabsrv/src/main/java/org/jabref/http/server/services/ServerUtils.java[57-90]
- jabsrv/src/main/java/org/jabref/http/server/resources/EntriesResource.java[53-88]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Absolute links & rename semantics 🐞 Bug ✓ Correctness
Description
• The LinkedFile created from tempFile stores the full path as the link; if
  renameToSuggestedName() returns false for non-error “no-op” cases, the link stays absolute and
  gets serialized into the BibEntry unchanged, reducing portability.
• renameToSuggestedName() returning false does not exclusively mean “rename failed” (it also
  returns false when renaming isn’t needed), but the endpoint treats all false results as a failure
  and returns a warning message.
Code

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[R165-182]

+        // 4. Create LinkedFile
+        LinkedFile linkedFile = new LinkedFile(tempFile.getFileName().toString(), tempFile, "PDF");
+
+        // 5. Rename to suggested pattern (e.g. Author - Title.pdf)
+        LinkedFileHandler fileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, preferences.getFilePreferences());
+        boolean renameSuccessful = fileHandler.renameToSuggestedName();
+        if (!renameSuccessful) {
+            LOGGER.warn("Renaming to suggested name failed. Keeping temp filename.");
+        }
+
+        // 6. Add to entry
+        entry.addFile(linkedFile);
+
+        if (renameSuccessful) {
+            return Response.noContent().build();
+        } else {
+            return Response.ok("File uploaded but could not be renamed to suggested pattern.").build();
+        }
Evidence
EntryResource creates a LinkedFile using a Path; LinkedFile stores that Path as link.toString()
(absolute here). LinkedFileHandler.renameToSuggestedName can return false for no-op cases (suggested
equals current, duplicate-mark logic, etc.) and only updates the link when it performs a rename.
FileFieldWriter writes entry.getLink() verbatim into the bib field, so absolute links will be
persisted if not changed.

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[165-182]
jablib/src/main/java/org/jabref/model/entry/LinkedFile.java[52-54]
jablib/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java[156-175]
jablib/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java[228-233]
jablib/src/main/java/org/jabref/logic/bibtex/FileFieldWriter.java[15-27]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The endpoint can persist absolute paths into the bib entry (depending on rename outcomes), and it interprets `renameToSuggestedName()` false returns as a failure even when it’s a no-op.

### Issue Context
- `new LinkedFile(..., Path link, ...)` stores `link.toString()`.
- `renameToSuggestedName()` returns false for multiple non-error cases.
- The LinkedFile link is only updated when a rename occurs.

### Fix Focus Areas
- jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[165-182]
- jablib/src/main/java/org/jabref/model/entry/LinkedFile.java[52-54]
- jablib/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java[156-186]
- jablib/src/main/java/org/jabref/logic/bibtex/FileFieldWriter.java[15-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. addFile lacks audit log 📘 Rule violation ✧ Quality
Description
• The new endpoint performs a critical write action (uploading a file and attaching it to a
  BibEntry) without emitting an audit-style log entry for success/failure.
• Without an audit trail, it is difficult to reconstruct who/what initiated uploads and when, which
  reduces diagnosability and accountability.
• Add a dedicated log entry for the upload attempt and outcome, including non-sensitive context
  (e.g., library id, entry citation key, resulting filename).
Code

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[R160-182]

+        // 3. Save stream to temporary file
+        String timestamp = ZonedDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
+        java.nio.file.Path tempFile = Files.createTempFile(targetDir, "jabref-upload-" + timestamp + "-", ".pdf");
+        Files.copy(fileInputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
+
+        // 4. Create LinkedFile
+        LinkedFile linkedFile = new LinkedFile(tempFile.getFileName().toString(), tempFile, "PDF");
+
+        // 5. Rename to suggested pattern (e.g. Author - Title.pdf)
+        LinkedFileHandler fileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, preferences.getFilePreferences());
+        boolean renameSuccessful = fileHandler.renameToSuggestedName();
+        if (!renameSuccessful) {
+            LOGGER.warn("Renaming to suggested name failed. Keeping temp filename.");
+        }
+
+        // 6. Add to entry
+        entry.addFile(linkedFile);
+
+        if (renameSuccessful) {
+            return Response.noContent().build();
+        } else {
+            return Response.ok("File uploaded but could not be renamed to suggested pattern.").build();
+        }
Evidence
The compliance checklist requires logging critical actions with sufficient context. The endpoint
copies an uploaded stream to disk and modifies an entry but only logs a warning when rename fails;
there is no log indicating an upload occurred, nor a success/failure audit record for the operation.

Rule 1: Generic: Comprehensive Audit Trails
jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[160-182]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new upload endpoint performs a critical write operation (file upload + entry modification) without an audit-style log entry that records the action and outcome.

## Issue Context
Current logging only covers the rename-failure case via `LOGGER.warn(...)`. Add logs for start/success/failure with safe context (library id, citation key, stored filename/path if appropriate) and ensure sensitive data is not logged.

## Fix Focus Areas
- jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[139-183]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. File dir not validated 🐞 Bug ⛯ Reliability
Description
• The chosen "existing file dir" is only checked for existence, not that it is a writable directory;
  misconfiguration can cause createTempFile/rename to fail with an internal error.
• The endpoint currently returns 400 only when no directory exists at all; adding a more precise
  check allows returning a clear client/server error message (and avoiding confusing 500s).
Code

jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[R153-163]

+        Optional<java.nio.file.Path> targetDirOpt = databaseContext.getFirstExistingFileDir(preferences.getFilePreferences());
+
+        if (targetDirOpt.isEmpty()) {
+            throw new BadRequestException("Library must be saved or have a file directory configured to attach files.");
+        }
+        java.nio.file.Path targetDir = targetDirOpt.get();
+
+        // 3. Save stream to temporary file
+        String timestamp = ZonedDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
+        java.nio.file.Path tempFile = Files.createTempFile(targetDir, "jabref-upload-" + timestamp + "-", ".pdf");
+        Files.copy(fileInputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
Evidence
BibDatabaseContext.getFirstExistingFileDir uses Files.exists only. EntryResource assumes that
returned path can be used as a directory for Files.createTempFile(targetDir, ...), which requires
a directory and appropriate permissions.

jablib/src/main/java/org/jabref/model/database/BibDatabaseContext.java[199-206]
jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[153-163]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`getFirstExistingFileDir` guarantees only that the path exists, not that it is a writable directory. The endpoint then assumes it can create files there.

### Issue Context
- `Files.createTempFile(Path dir, ...)` requires `dir` to be a directory and writable.

### Fix Focus Areas
- jabsrv/src/main/java/org/jabref/http/server/resources/EntryResource.java[152-163]
- jablib/src/main/java/org/jabref/model/database/BibDatabaseContext.java[199-206]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@koppor
Copy link
Copy Markdown
Member

koppor commented Feb 5, 2026

qodo feedback can be adressed as follow up

@koppor koppor added this pull request to the merge queue Feb 5, 2026
@github-actions github-actions bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label Feb 5, 2026
Merged via the queue into JabRef:main with commit 8faede9 Feb 5, 2026
56 of 59 checks passed
Siedlerchr added a commit to Jalina2007/jabref that referenced this pull request Feb 5, 2026
…4902

* upstream/main: (23 commits)
  Some more recipes from OpenRewrite (JabRef#15030)
  feat: Add PDF Upload endpoint to EntryResource (JabRef#14963)
  Heuristics also used at batch (JabRef#15025)
  Fix cleanup-pr.yml
  New Crowdin updates (JabRef#15035)
  Use patched Gradle version (JabRef#15034)
  Add OpenAlex-based Citation Fetcher (JabRef#15023)
  Update null annotaitons at EntryBasedFetcher (JabRef#15024)
  Fix CHANGELOG.md test
  Use _ for unused variables (JabRef#15028)
  Use ubuntu-latest for checkstyle and javadoc
  Update Gradle Wrapper from 9.3.0-jabref-2 to 9.3.1 (JabRef#15021)
  Use "ubuntu-slim" for most workflows (JabRef#15019)
  Refine GroupsTree (JabRef#15013)
  New Crowdin updates (JabRef#15018)
  Added Clear group option (JabRef#15017)
  Chore(deps): Bump com.uber.nullaway:nullaway from 0.12.15 to 0.13.1 in /versions (JabRef#15006)
  Chore(deps): Bump tools.jackson:jackson-bom in /versions (JabRef#15007)
  No rush in Docker building
  Yaml issue workaround
  ...
Siedlerchr added a commit that referenced this pull request Feb 8, 2026
…es/jablib/src/main/resources/csl-styles-6c79ffe

* upstream/main: (68 commits)
  Chore(deps): Bump org.apache.httpcomponents.client5:httpclient5 (#15060)
  Chore(deps): Bump com.google.errorprone:error_prone_core in /versions (#15059)
  Chore(deps): Bump de.undercouch.download:de.undercouch.download.gradle.plugin (#15057)
  Chore(deps): Bump org.postgresql:postgresql in /versions (#15058)
  Chore(deps): Bump de.undercouch.download:de.undercouch.download.gradle.plugin (#15056)
  Updates on Wednesday, not on Sunday
  Add screenshot requirement (#15050)
  Switch image for javadoc
  Better docker layer caching during build (#15042)
  New Crowdin updates (#15045)
  Chore: reuse shared 'setup-gradle' in all places in test-code.yml (#15043)
  Chore: add 'testlens-app/setup-testlens' GH action (#15044)
  Add: HTTP Server and LSP server toggles to quick settings (#14972)
  Some more recipes from OpenRewrite (#15030)
  feat: Add PDF Upload endpoint to EntryResource (#14963)
  Heuristics also used at batch (#15025)
  Fix cleanup-pr.yml
  New Crowdin updates (#15035)
  Use patched Gradle version (#15034)
  Add OpenAlex-based Citation Fetcher (#15023)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: changes-required Pull requests that are not yet complete status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants