Skip to content

Fix 403 forbidden error when following redirect - #229

Open
fezster wants to merge 6 commits into
nylonee:mainfrom
fezster:main
Open

fezster wants to merge 6 commits into
nylonee:mainfrom
fezster:main

Conversation

@fezster

@fezster fezster commented Jan 25, 2026 •

Copy link
Copy Markdown

Description

Fix 403 forbidden error when following redirect.

Remove Host header which is rejected when redirect followed. Instead, let http4s set the Host header

Checklist

  • Documentation Updated
  • sbt scalafmtAll Run (and optionally sbt scalafmtSbt)
  • At least one approval from a codeowner

Summary by CodeRabbit

Release Notes

  • New Features

    • Added configurable Radarr minimum availability for movie imports.
    • Application version is now displayed during startup.
    • Docker deployments support runtime configuration through the entrypoint.
  • Bug Fixes

    • Improved HTTP request compatibility by removing the manually specified Host header.
  • Chores

    • Upgraded Docker image setup with multi-stage builds, caching, and a dedicated runtime environment.
    • Updated the application version to 0.2.7.
    • Added request payload logging for Radarr and Sonarr integrations.

@coderabbitai

coderabbitai Bot commented Jan 25, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request updates Docker image construction, removes the HTTP Host header, generates and logs the application version, adds optional Radarr availability configuration, and logs outgoing Radarr and Sonarr JSON payloads.

Changes

Container and HTTP updates

Layer / File(s) Summary
Docker runtime and build stages
docker/Dockerfile, docker/entrypoint.sh
The Dockerfile uses separate build and runtime stages, caches build inputs, uses Eclipse Temurin, and installs the entrypoint. The entrypoint passes RADARR_AVAILABILITY when set.
HTTP request headers
src/main/scala/http/HttpClient.scala
The HTTP client no longer sends the Host header. It retains the User-Agent header.

Application version metadata

Layer / File(s) Summary
Version generation and startup logging
build.sbt, src/main/scala/Server.scala
The build generates version.properties. Server.run loads the version, uses unknown when absent, closes the resource stream, and logs the version before startup.

Radarr availability configuration

Layer / File(s) Summary
Configuration propagation
src/main/scala/configuration/Keys.scala, src/main/scala/configuration/Configuration.scala, src/main/scala/configuration/ConfigurationUtils.scala, src/main/scala/configuration/ConfigurationRedactor.scala, docker/entrypoint.sh
The configuration reads a trimmed, non-empty radarr.availability value and displays it in redacted output. The entrypoint passes the value as a system property when configured.
Radarr request serialization
src/main/scala/radarr/RadarrPost.scala, src/main/scala/radarr/RadarrUtils.scala
RadarrPost adds optional minimumAvailability. The encoder includes the field only when configured. Radarr movie creation passes the configured value.
Arr payload logging
src/main/scala/radarr/RadarrUtils.scala, src/main/scala/sonarr/SonarrUtils.scala
Radarr and Sonarr log outgoing JSON payloads before sending POST requests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Entrypoint
  participant ConfigurationUtils
  participant RadarrUtils
  participant RadarrAPI
  Entrypoint->>ConfigurationUtils: Pass radarr.availability
  ConfigurationUtils->>RadarrUtils: Provide optional radarrAvailability
  RadarrUtils->>RadarrUtils: Build RadarrPost with minimumAvailability
  RadarrUtils->>RadarrAPI: Log and send serialized movie payload
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: fixing HTTP 403 errors caused by the Host header during redirects.
Description check ✅ Passed The description explains the Host header change and includes all required template sections and checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/main/scala/http/HttpClient.scala`:
- Around line 23-26: The current FollowRedirect usage sets sensitiveHeaderFilter
= _ => true which forwards all sensitive headers to every redirect; update the
filter used in FollowRedirect (the call that constructs
FollowRedirect[IO](maxRedirects = 10, sensitiveHeaderFilter = ...)(c)) to only
allow sensitive headers for same-origin redirects or a trusted-host whitelist:
implement a predicate that compares the original request URI host (and
scheme/port if needed) against the redirect target host and returns true only
when they match (or when the redirect host is in a configured trustedDomains
set), and pass that predicate as sensitiveHeaderFilter so
Authorization/Cookie/X-Api-Key/X-Plex-Token are not sent to cross-origin
redirects.

Comment thread src/main/scala/http/HttpClient.scala Outdated
Remove Host header which is rejected when redirect followed. Instead, let http4s set the Host header
@fezster
fezster marked this pull request as draft January 26, 2026 09:29
@fezster
fezster marked this pull request as ready for review January 26, 2026 09:29
@zMastaa

zMastaa commented Feb 21, 2026

Copy link
Copy Markdown

Is the project abandoned ?

@albinmedoc

Copy link
Copy Markdown

@nylonee, can't we merge this to get the application working again?

@revan415

revan415 commented Mar 6, 2026

Copy link
Copy Markdown

Please merge the PR!

@malon64

malon64 commented Mar 12, 2026 •

Copy link
Copy Markdown

@nylonee @fezster can we have this PR merged so we get the service working again ? 🥺 🥺 🥺 i built my docker image on it and it fixed the problem for me, but it is sad that the project is abandoned

@fezster

fezster commented Mar 13, 2026

Copy link
Copy Markdown
Author

@nylonee @fezster can we have this PR merged so we get the service working again ? 🥺 🥺 🥺 i built my docker image on it and it fixed the problem for me, but it is sad that the project is abandoned

I need someone to approve the PR before I can merge

fezster added 2 commits April 14, 2026 17:53
Adds RADARR_AVAILABILITY env var to control movie availability in Radarr.
When set (e.g. RADARR_AVAILABILITY=announced), the minimumAvailability
field is included in the POST payload to /api/v3/movie. Omitted when
not set to avoid null serialization.

Also adds startup version log.
…alues.

Line 191 accepts raw Option[String] without normalization. If the runtime injects an empty property value (e.g., -Dradarr.availability=), this becomes Some(""). This value propagates through to RadarrPost.scala (line 24-26), where it serializes as "minimumAvailability": "" in the JSON payload, which the Radarr API does not accept.
@ryan910

ryan910 commented May 6, 2026

Copy link
Copy Markdown

We need to get this approved

Comment thread docker/Dockerfile
RUN chmod a+rx /app/target/universal/stage/bin/*

FROM openjdk:11-jre-slim
FROM eclipse-temurin:11-jre-jammy

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Was this necessary to get the image to build? If not removing these changes would make the change smaller and easier to get merged (although it doesn't appear like the size of the change is the issue and this is abandoned)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, was necessary as openjdk has been deprecated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/scala/configuration/ConfigurationUtils.scala`:
- Around line 191-192: Update the Radarr configuration loading flow around the
availability value in ConfigurationUtils so, after trimming and ignoring empty
values, only tba, announced, inCinemas, released, and deleted are accepted.
Reject unsupported non-empty values during startup with a clear configuration
error, while preserving the existing optional behavior for absent or blank
availability.

In `@src/main/scala/radarr/RadarrPost.scala`:
- Around line 15-29: Update the RadarrPost encoder to include an addOptions
object containing searchForMovie set to true in the serialized movie payload.
Preserve the existing fields and optional minimumAvailability handling while
ensuring every added movie requests an immediate search.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 73c0bdee-af1d-4a1d-aa4d-eb1f9ed4b0a4

📥 Commits

Reviewing files that changed from the base of the PR and between 3c548f2 and 1a2e0f7.

📒 Files selected for processing (9)
  • build.sbt
  • docker/entrypoint.sh
  • src/main/scala/Server.scala
  • src/main/scala/configuration/Configuration.scala
  • src/main/scala/configuration/ConfigurationRedactor.scala
  • src/main/scala/configuration/ConfigurationUtils.scala
  • src/main/scala/configuration/Keys.scala
  • src/main/scala/radarr/RadarrPost.scala
  • src/main/scala/radarr/RadarrUtils.scala

Comment on lines +191 to +192
availability = configReader.getConfigOption(Keys.radarrAvailability).map(_.trim).filter(_.nonEmpty)
} yield (url, apiKey, qualityProfileId, rootFolder, tagIds, availability)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Show the supported Radarr values.
curl -fsSL \
  'https://raw.githubusercontent.com/Radarr/Radarr/develop/src/Radarr.Api.V3/openapi.json' |
  jq -r '.components.schemas.MovieStatusType.enum[]'

# Trace validation and error-reporting paths.
rg -n -C4 --glob '*.scala' \
  'radarrAvailability|minimumAvailability|Sent .* to Radarr|Received warning for sending' .

Repository: nylonee/watchlistarr

Length of output: 9455


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect imports/dependencies and exact call sites relevant to validation and HTTP response handling.
rg -n --glob '*.scala' 'case class RadarrConfiguration|def postToArr|EitherT|Client|httpClient|raiseFailures' .
git ls-files '*build*' | sed -n '1,120p'
cat -n src/main/scala/configuration/ConfigurationUtils.scala | sed -n '160,200p'
cat -n src/main/scala/configuration/Configuration.scala | sed -n '1,80p'
if [ -f src/main/scala/radarr/RadarrUtils.scala ]; then
  cat -n src/main/scala/radarr/RadarrUtils.scala | sed -n '1,70p'
fi
if [ -f src/main/scala/radarr/RadarrPost.scala ]; then
  cat -n src/main/scala/radarr/RadarrPost.scala | sed -n '1,40p'
fi

# Check whether the repository has any existing enum-like helper for Radarr availability.
rg -n --glob '*.scala' 'tba|announced|inCinemas|released|deleted|MovieStatusType|RadarrAvailability|radarrAvailability' .

Repository: nylonee/watchlistarr

Length of output: 35372


Reject unsupported Radarr availability values during configuration loading.

radarr.availability currently accepts any non-empty string and sends it as minimumAvailability. Radarr only supports tba, announced, inCinemas, released, and deleted. A typo or unsupported value will silently be logged as a warning, then reported as successfully sent. Validate this field before startup and fail with a clear error for invalid values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scala/configuration/ConfigurationUtils.scala` around lines 191 -
192, Update the Radarr configuration loading flow around the availability value
in ConfigurationUtils so, after trimming and ignoring empty values, only tba,
announced, inCinemas, released, and deleted are accepted. Reject unsupported
non-empty values during startup with a clear configuration error, while
preserving the existing optional behavior for absent or blank availability.

Comment on lines +15 to +29
object RadarrPost {
implicit val encoder: Encoder[RadarrPost] = Encoder.instance { post =>
var fields = List[(String, Json)](
"title" -> post.title.asJson,
"tmdbId" -> post.tmdbId.asJson,
"qualityProfileId" -> post.qualityProfileId.asJson,
"rootFolderPath" -> post.rootFolderPath.asJson,
"tags" -> post.tags.asJson
)
post.minimumAvailability.foreach { avail =>
fields = fields :+ ("minimumAvailability" -> avail.asJson)
}
Json.fromJsonObject(JsonObject.fromIterable(fields))
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C4 --glob '*.scala' \
  'AddOptions|addOptions|searchForMovie|RadarrPost' .

curl -fsSL \
  'https://raw.githubusercontent.com/Radarr/Radarr/develop/src/Radarr.Api.V3/openapi.json' |
  jq -e '.components.schemas.AddMovieOptions.properties.searchForMovie'

Repository: nylonee/watchlistarr

Length of output: 7623


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- RadarrPost.scala ---'
cat -n src/main/scala/radarr/RadarrPost.scala

printf '%s\n' '--- RadarrUtils.scala relevant section ---'
cat -n src/main/scala/radarr/RadarrUtils.scala

printf '%s\n' '--- Radarr delete post addOptions (context) ---'
cat -n src/main/scala/radarr/RadarrDelete.scala

printf '%s\n' '--- json output from current encoder ---'
python3 - <<'PY'
from pathlib import Path
src = Path('src/main/scala/radarr/RadarrPost.scala').read_text()
print('addOptions field in RadarrPost.scala:', 'addOptions' in src)
print('searchForMovie in RadarrPost.scala:', 'searchForMovie' in src)
PY

Repository: nylonee/watchlistarr

Length of output: 6063


🌐 Web query:

Radarr API add movie addOptions searchForMovie default true

💡 Result:

To trigger an automatic search when adding a movie via the Radarr API, you must include the addOptions object in your POST request body with the searchForMovie property set to true [1][2][3]. The correct JSON structure for the request body is as follows: { "title": "Movie Title", "tmdbId": 12345, "qualityProfileId": 1, "rootFolderPath": "/path/to/movies", "monitored": true, "addOptions": { "searchForMovie": true } } When adding a movie via the POST /api/v3/movie endpoint, the searchForMovie option is not set to true by default [1]. Including this option explicitly in the payload instructs Radarr to perform an immediate search for the movie upon addition [1][2]. This mechanism is widely used to replicate the behavior available in the Radarr Web UI where users can check an option to automatically search for the movie once it is added [1].

Citations:


Set searchForMovie explicitly when adding movies to Radarr.

RadarrPost no longer sends addOptions, so Radarr will not perform the automatic search after POST /api/v3/movie. Add addOptions: { "searchForMovie": true } unless the target integration is not intended to search immediately after adding a movie.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scala/radarr/RadarrPost.scala` around lines 15 - 29, Update the
RadarrPost encoder to include an addOptions object containing searchForMovie set
to true in the serialized movie payload. Preserve the existing fields and
optional minimumAvailability handling while ensuring every added movie requests
an immediate search.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/scala/sonarr/SonarrUtils.scala`:
- Line 95: Update the Sonarr request logging around the logger.info call to
avoid emitting the full payload at INFO level; log only the endpoint and request
status there, and move any detailed payload diagnostics to DEBUG using a
redacted payload.
- Line 95: Move the Sonarr request log in postToArr into the IO effect that
performs the request, ensuring it executes only when the request is run rather
than while the EitherT is built. Preserve the existing request behavior and
avoid logging the payload during lazy construction, failure, or cancellation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ff05e4b-e5d2-401b-8c9c-823247c2fcd2

📥 Commits

Reviewing files that changed from the base of the PR and between 1a2e0f7 and 3253fcc.

📒 Files selected for processing (2)
  • src/main/scala/radarr/RadarrUtils.scala
  • src/main/scala/sonarr/SonarrUtils.scala
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/scala/radarr/RadarrUtils.scala

client: HttpClient
)(baseUrl: Uri, apiKey: String, endpoint: String)(payload: Json): EitherT[IO, Throwable, T] =
)(baseUrl: Uri, apiKey: String, endpoint: String)(payload: Json): EitherT[IO, Throwable, T] = {
logger.info(s"Sending JSON to Sonarr: $payload")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log the full payload at INFO level.

Line [95] writes the complete Sonarr JSON payload to application logs. The payload includes media metadata and configuration values such as titles, identifiers, root folders, profile IDs, and tags. Retained or shared logs can expose user library information and filesystem paths.

Log only the endpoint and request status at INFO level. Use a redacted payload at DEBUG level only when detailed diagnostics are required.

Proposed fix
-    logger.info(s"Sending JSON to Sonarr: $payload")
+    logger.info(s"Sending request to Sonarr endpoint: $endpoint")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(s"Sending JSON to Sonarr: $payload")
logger.info(s"Sending request to Sonarr endpoint: $endpoint")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scala/sonarr/SonarrUtils.scala` at line 95, Update the Sonarr
request logging around the logger.info call to avoid emitting the full payload
at INFO level; log only the endpoint and request status there, and move any
detailed payload diagnostics to DEBUG using a redacted payload.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify that the Sonarr log is sequenced inside the returned effect.
rg -n -C 8 'logger\.info\(s"Sending JSON to Sonarr|def postToArr|client\.httpRequest' src/main/scala

Repository: nylonee/watchlistarr

Length of output: 17884


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SonarrUtils context =="
sed -n '1,140p' src/main/scala/sonarr/SonarrUtils.scala

echo
echo "== postToArr call sites =="
rg -n -C 4 'postToArr\b|EitherT\(client\.httpRequest' src/main/scala/sonarr src/main/scala/radarr src/main/scala

echo
echo "== Cats IO/EitherT availability in project files =="
rg -n 'cats-effect|typelevel|cats' build.sbt project . --glob '!target/**' --glob '!node_modules/**' 2>/dev/null || true

Repository: nylonee/watchlistarr

Length of output: 21767


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Use an embedded REPL-free behavioral probe that treats source text as data.
# It checks whether the INFO log is syntactically outside the IO/EitherT effect body.
python3 - <<'PY'
from pathlib import Path
import re

path = Path('src/main/scala/sonarr/SonarrUtils.scala')
src = path.read_text()

def extract_function_method(method_name: str):
    # Find the declaration and then consume balanced braces, with support for string interpolation chars.
    # For demonstration, the SonarrUtils method is simple enough that string literal balance is sufficient.
    pattern = re.compile(f'\\bdef\\s+{re.escape(method_name)}\\b\\s*\\([^)]*\\)\\s*[^=]*=\\s*\\n')
    m = pattern.search(src)
    if not m:
        return None
    start = m.start()
    i = src.find('{', m.end())
    if i == -1:
        return None
    depth = 0
    squote = dquote = raw = rawd = False
    while i < len(src):
        c = src[i]
        cc = src[i:i+2]
        if raw:
            if c == '`': raw = False
        elif rawd:
            if src.startswith('"""', i): rawd = False; i += 2; continue
        elif cc == '`'': raw = True; i += 1; continue
        elif cc == '"""': rawd = True; continue
        elif c == '"' and not dquote:
            dquote = True
        elif c == '"' and dquote:
            dquote = False
        elif cc == '{{': i += 1; continue
        elif cc == '}}': i += 1; continue
        else:
            if c == '{': depth += 1
            elif c == '}':
                depth -= 1
                if depth == 0:
                    end = i + 1
                    break
        i += 1
    return src[start:end], start, end

body, start, end = extract_function_method('postToArr')
print(f'function_offset_start={start}')
print(f'log_offset={src.find("logger.info(s\"Sending JSON to Sonarr:", start, end)}')
print(f'for_offset={src.find("for {", start, end)}')

inside_brace = body.index('logger.info(s"Sending JSON to Sonarr:') < body.index('for {')
# The method assigns into a for-comprehension; the returned effect starts at the for, not before it.
returned_effect_start = body.index('for {')
print(f'log_before_for={inside_brace}')
print(f'log_inside_returned_effect_body={inside_brace}')
print(f'postToArr_return_effect_starts_at_for={returned_effect_start > start}')
PY

Repository: nylonee/watchlistarr

Length of output: 332


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Exact Sonarr log position =="
python3 - <<'PY'
from pathlib import Path
text = Path('src/main/scala/sonarr/SonarrUtils.scala').read_text()
needle = 'logger.info(s"Sending JSON to Sonarr:'
start = text.find(needle, text.index('private def postToArr'))
end = text.index('\n}', start) + 2
lines = text.splitlines()
for_offset = text.find('for {', text.indexOf('private def postToArr') if hasattr(text,'indexOf') else text.index('private def postToArr'))
if start != -1:
    line_no = text[:text.find(needle, text.index('private def postToArr'))].count('\n') + 1
    print(f'log_at_line={line_no}')
    print(f'for_at_block={text.find("for {", start)}')
PY

echo
echo "== Read-only line-number sanity check from git diff against local file =="
(git diff -- src/main/scala/sonarr/SonarrUtils.scala | sed -n '1,80p' || true)
sed -n '92,103p' src/main/scala/sonarr/SonarrUtils.scala

Repository: nylonee/watchlistarr

Length of output: 869


Move the Sonarr request log inside the IO effect.

Line 95 logs while postToArr builds the EitherT, before the request effect runs. A lazy failed or canceled IO can still log Sending JSON to Sonarr:... and expose the full payload in retained logs.

Proposed fix
   )(baseUrl: Uri, apiKey: String, endpoint: String)(payload: Json): EitherT[IO, Throwable, T] = {
-    logger.info(s"Sending JSON to Sonarr: $payload")
     for {
+      _ <- EitherT.liftF[IO, Throwable, Unit](IO(logger.info(s"Sending JSON to Sonarr: $payload")))
       response <- EitherT(
         client.httpRequest(Method.POST, baseUrl / "api" / "v3" / endpoint, Some(apiKey), Some(payload))
       )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(s"Sending JSON to Sonarr: $payload")
)(baseUrl: Uri, apiKey: String, endpoint: String)(payload: Json): EitherT[IO, Throwable, T] = {
for {
_ <- EitherT.liftF[IO, Throwable, Unit](IO(logger.info(s"Sending JSON to Sonarr: $payload")))
response <- EitherT(
client.httpRequest(Method.POST, baseUrl / "api" / "v3" / endpoint, Some(apiKey), Some(payload))
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scala/sonarr/SonarrUtils.scala` at line 95, Move the Sonarr request
log in postToArr into the IO effect that performs the request, ensuring it
executes only when the request is run rather than while the EitherT is built.
Preserve the existing request behavior and avoid logging the payload during lazy
construction, failure, or cancellation.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
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.

7 participants