| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
@Vishalsahani156 is attempting to deploy a commit to the vernu's projects Team on Vercel. A member of the Team first needs to authorize it. |
Sorry, something went wrong.
📝 Walkthrough
WalkthroughThe PR adds boolean and numeric configuration coercion for SMS queue settings. It updates SMS workers to use WorkManager retry counts and retry malformed JSON responses. Status updates retain status-specific unique work. RecyclerView actions use the current adapter position. ChangesConfiguration coercion and SMS worker behavior
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Possibly related PRs
❌ Failed checks (1 warning)
Comment @coderabbitai help to get the list of available commands. |
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agentsVerify 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 @.env: - Line 6: Keep NEXT_PUBLIC_API_BASE_URL as the browser-facing endpoint, add a separate internal API URL using the textbee-api service and port 3001, and update the server client in httpServerClient.ts to read that internal configuration for server-side requests. In `@android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt`: - Around line 40-42: Update the uniqueWorkName construction near beginUniqueWork to replace the System.currentTimeMillis() fallback used when smsDTO.smsId is absent with a collision-free identifier such as workRequest.id.toString() or a UUID, while preserving the existing SMS ID and status components. - Around line 59-62: Update the retry guard in SMSStatusUpdateWorker so it returns Result.failure() only when runAttemptCount is greater than MAX_RETRIES, allowing five retries after the initial attempt while preserving the existing maximum-retry log and failure behavior. - Around line 78-80: Wrap the KEY_SMS_DTO Gson().fromJson(...) deserialization in a separate try/catch for JsonSyntaxException before the API try block. Log the malformed response using the existing TAG and return Result.retry(), while leaving the surrounding API error handling unchanged. In `@api/src/common/config-coerce.ts`: - Around line 11-25: Update the boolean and numeric coercion helpers around getNumber and the adjacent boolean getter to trim string inputs before checking for emptiness, so whitespace-only values return fallback rather than being parsed. Preserve direct boolean handling and valid trimmed values, and add tests covering whitespace-only boolean and numeric configuration values. In `@api/src/gateway/queue/sms-queue.service.ts`: - Around line 20-24: Update the MAX_SMS_BATCH_SIZE initialization in the SMS queue service constructor to accept only positive integer values; fall back to 100 when getNumber returns zero, a negative number, a fraction, or otherwise invalid input, while preserving valid configured batch sizes. In `@api/src/main.ts`: - Around line 72-75: Update the Firebase initialization guard around firebase.initializeApp to require firebaseConfig.projectId, firebaseConfig.clientEmail, and firebaseConfig.privateKey before calling firebase.credential.cert(). When any credential is absent, skip initialization and log that the Firebase configuration is incomplete.
Fix all unresolved CodeRabbit comments on this PR:
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: bb8c1b67-e98d-4de0-affd-4e30defbc553
📥 CommitsReviewing files that changed from the base of the PR and between bf0197e and cc3f58f.
📒 Files selected for processing (14)Commit Status: Vercel: Vercel
Conclusion: failure
Authorization required to deploy.
[warning] 5-5: [UnorderedKey] The API_PORT key should go before the REDIS_PORT key
(UnorderedKey)
[warning] 6-6: [UnorderedKey] The NEXT_PUBLIC_API_BASE_URL key should go before the REDIS_PORT key
(UnorderedKey)
🔇 Additional comments (5)docker-compose.yaml (1).gitignore (1)51-51: LGTM!
Also applies to: 73-73
api/test/app.e2e-spec.ts (1)7-7: LGTM!
android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt (2)3-3: LGTM!
10-10: LGTM!
Also applies to: 22-27
40-42: 🗄️ Data Integrity & Integration
Verify ordering across status-specific work.
uniqueWorkName includes smsDTO.status, so KEEP deduplicates only matching statuses. A retry for an older status and a new request for a newer status use different names. Both can call GatewayApiService.java at Lines 25-28. If the older request finishes last, it can overwrite the newer server state.
Confirm that the gateway rejects stale transitions, or serialize work by smsId and enforce status ordering. WorkManager’s unique-work policy is scoped to each unique name. (developer.android.com)
Source: MCP tools
Sorry, something went wrong.
| REDIS_PORT=6380 | ||
| WEB_PORT=3005 | ||
| API_PORT=3006 | ||
| NEXT_PUBLIC_API_BASE_URL=http://localhost:3006/api/v1 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Use separate API URLs for browser and server requests.
At Line 6, http://localhost:3006/api/v1 works in the browser. The server-side client also reads this value inside textbee-web, where localhost:3006 points to the web container. Server-side API requests cannot reach textbee-api.
Keep NEXT_PUBLIC_API_BASE_URL for browser traffic. Add an internal URL such as http://textbee-api:3001/api/v1 for web/lib/httpServerClient.ts.
🧰 Tools 🪛 dotenv-linter (4.0.0)[warning] 6-6: [UnorderedKey] The NEXT_PUBLIC_API_BASE_URL key should go before the REDIS_PORT key
(UnorderedKey)
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.env at line 6, Keep NEXT_PUBLIC_API_BASE_URL as the browser-facing endpoint, add a separate internal API URL using the textbee-api service and port 3001, and update the server client in httpServerClient.ts to read that internal configuration for server-side requests.
Sorry, something went wrong.
| val uniqueWorkName = "sms_status_${smsDTO.smsId ?: System.currentTimeMillis()}_${smsDTO.status}" | ||
| WorkManager.getInstance(context) | ||
| .beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.REPLACE, workRequest) | ||
| .beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.KEEP, workRequest) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- target file ---'
sed -n '1,120p' android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt
printf '%s\n' '--- related symbols ---'
rg -n -C 4 'SMSStatusUpdateWorker|smsDTO|beginUniqueWork|ExistingWorkPolicy' android/app/src/main/java
printf '%s\n' '--- WorkRequest construction ---'
rg -n -C 5 'OneTimeWorkRequest|workRequest|SMSStatus' android/app/src/main/java/com/vernu/smsRepository: vernu/textbee
Length of output: 50369
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
for p in Path("android").rglob("*.kt"):
text = p.read_text(errors="ignore")
if "SMSStatusUpdateWorker" in text or "beginUniqueWork" in text or "workRequest" in text:
print(f"--- {p} ---")
for i, line in enumerate(text.splitlines(), 1):
if any(x in line for x in ("SMSStatusUpdateWorker", "beginUniqueWork", "workRequest", "smsDTO")):
lo, hi = max(1, i-3), i+5
lines = text.splitlines()
for n in range(lo, min(len(lines), hi)+1):
print(f"{n}: {lines[n-1]}")
print()
PYRepository: vernu/textbee
Length of output: 33392
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- SMSDTO and status enqueue call sites ---'
rg -n -C 6 'class SMSDTO|data class SMSDTO|smsId|createSentPendingIntent|createDeliveredPendingIntent|sendTextMessage' \
android/app/src/main/java/com/vernu/sms/dtos \
android/app/src/main/java/com/vernu/sms/helpers/SMSHelper.kt \
android/app/src/main/java/com/vernu/sms/receivers/SMSStatusReceiver.kt
printf '%s\n' '--- relevant project configuration/dependencies ---'
rg -n -C 2 'work-runtime|androidx.work|kotlin' android --glob 'build.gradle*' --glob 'libs.versions.toml' --glob 'gradle.properties'
printf '%s\n' '--- exact target lines ---'
sed -n '32,48p' android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.ktRepository: vernu/textbee
Length of output: 27965
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from uuid import uuid4
def current_name(sms_id, status, now_ms, request_id):
return f"sms_status_{sms_id if sms_id is not None else now_ms}_{status}"
same_millisecond = 1_735_000_000_000
first = current_name(None, "SENT", same_millisecond, uuid4())
second = current_name(None, "SENT", same_millisecond, uuid4())
print("timestamp fallback names equal:", first == second)
print("name:", first)
first_id = str(uuid4())
second_id = str(uuid4())
first_fixed = f"sms_status_{first_id}_SENT"
second_fixed = f"sms_status_{second_id}_SENT"
print("request-ID fallback names equal:", first_fixed == second_fixed)
print("request IDs distinct:", first_id != second_id)
PYRepository: vernu/textbee
Length of output: 290
Use a collision-free fallback when smsDTO.smsId is absent.
Two calls within the same millisecond can generate the same uniqueWorkName. With ExistingWorkPolicy.KEEP, WorkManager ignores the second request while matching work remains pending. Use workRequest.id.toString() or a UUID for the fallback.
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt` around lines 40 - 42, Update the uniqueWorkName construction near beginUniqueWork to replace the System.currentTimeMillis() fallback used when smsDTO.smsId is absent with a collision-free identifier such as workRequest.id.toString() or a UUID, while preserving the existing SMS ID and status components.
Source: MCP tools
Sorry, something went wrong.
| } catch (e: JsonSyntaxException) { | ||
| Log.e(TAG, "Malformed response body: ${e.message}") | ||
| Result.retry() |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain🏁 Script executed:
#!/bin/bash
set -eu
file="android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt"
printf '%s\n' "== file excerpt =="
cat -n "$file" | sed -n '1,120p'
printf '%s\n' "== Gson and worker references =="
rg -n -C 3 'SMSStatusUpdateWorker|fromJson|JsonSyntaxException|KEY_SMS_DTO|Result\.retry|execute\(' android/app/src/main/java android/app/src/test android/app/src/androidTest 2>/dev/null || true
printf '%s\n' "== declared Gson versions =="
rg -n -C 2 'com\.google\.code\.gson|gson' --glob '!*build*' .Repository: vernu/textbee
Length of output: 29478
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' "== dependency declarations =="
find android -maxdepth 4 -type f \( -name 'build.gradle' -o -name 'build.gradle.kts' -o -name 'libs.versions.toml' -o -name 'gradle.properties' \) -print
rg -n -C 3 'gson|converter-gson|retrofit|version' android --glob 'build.gradle*' --glob 'libs.versions.toml' --glob 'gradle.properties' || true
printf '%s\n' "== SMS status service declarations =="
rg -n -C 8 'updateSMSStatus|SMSForwardResponseDTO|SMSStatus' android/app/src/main
printf '%s\n' "== DTO definitions =="
fd -i 'SMSDTO|SMSForwardResponseDTO' android/app/src/main
for f in $(fd -i 'SMSDTO|SMSForwardResponseDTO' android/app/src/main); do
echo "== $f =="
cat -n "$f"
doneRepository: vernu/textbee
Length of output: 34123
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
path = Path("android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt")
lines = path.read_text().splitlines()
from_json = next(i for i, line in enumerate(lines, 1) if "Gson().fromJson(smsDtoJson" in line)
try_line = next(i for i, line in enumerate(lines, 1) if line.strip() == "return try {")
# This source-level verifier checks the relevant lexical ordering and catch scope.
catch_lines = [i for i, line in enumerate(lines, 1)
if line.strip().startswith("} catch (e: JsonSyntaxException)")]
print({
"fromJson_line": from_json,
"api_try_line": try_line,
"json_syntax_catch_lines": catch_lines,
"fromJson_before_api_try": from_json < try_line,
"fromJson_inside_api_try_by_line_range": try_line < from_json < catch_lines[-1],
})
PYRepository: vernu/textbee
Length of output: 307
🌐 Web query:
Gson 2.9.0 Gson.fromJson(String, Class) JsonSyntaxException malformed JSON Javadoc; Retrofit 2.9.0 GsonResponseBodyConverter JsonSyntaxException execute behavior
💡 Result:
In Gson 2.9.0, the javadoc for Gson.fromJson(String, Class) states that the method throws JsonSyntaxException if the input is not a valid representation for an object of the specified class [1]. However, in practice, Gson often parses JSON leniently due to legacy design choices, meaning it may not throw a JsonSyntaxException for all forms of malformed JSON [2][3][4]. Regarding Retrofit 2.9.0, the GsonResponseBodyConverter is responsible for converting the response body using the provided Gson instance [5][6]. It executes the conversion by creating a new JsonReader and using an adapter to read the JSON stream [5]. If the JSON is syntactically invalid, the underlying Gson parsing process may throw a JsonSyntaxException, which propagates up through the Retrofit call [7][8]. Additionally, the GsonResponseBodyConverter explicitly checks if the JSON document was fully consumed; if not, it throws a JsonIOException [5]. It is important to note that when using Retrofit 2.9.0, the version of Gson included may vary depending on the dependencies configured in your project (e.g., using Gson 2.8.5 with Retrofit 2.9.0) [9]. If you encounter issues with malformed JSON not being rejected, it is generally recommended to inspect the raw response body or use strict parsing configurations (available in newer versions of Gson) if possible [3][4].
Citations:
Catch JsonSyntaxException around KEY_SMS_DTO deserialization.
Gson().fromJson(...) runs before the API try block. Gson 2.9.0 can throw JsonSyntaxException for invalid input, so the current catch does not handle rejected DTO data. Add a separate parse try/catch and log message.
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.kt` around lines 78 - 80, Wrap the KEY_SMS_DTO Gson().fromJson(...) deserialization in a separate try/catch for JsonSyntaxException before the API try block. Log the malformed response using the existing TAG and return Result.retry(), while leaving the surrounding API error handling unchanged.
Source: MCP tools
Sorry, something went wrong.
| const raw = config.get<string | boolean>(key) | ||
| if (raw === undefined || raw === null || raw === '') return fallback | ||
| if (typeof raw === 'boolean') return raw | ||
| return String(raw).trim().toLowerCase() === 'true' | ||
| } | ||
|
|
||
| export function getNumber( | ||
| config: ConfigService, | ||
| key: string, | ||
| fallback: number, | ||
| ): number { | ||
| const raw = config.get<string | number>(key) | ||
| if (raw === undefined || raw === null || raw === '') return fallback | ||
| const n = Number(raw) | ||
| return Number.isFinite(n) ? n : fallback |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Treat whitespace-only values as absent.
A value such as MAX_SMS_BATCH_SIZE=' ' bypasses the empty check. Number(' ') returns 0 instead of the fallback. This can pass a zero batch size to SmsQueueService.
Trim string values before the empty check. Add tests for whitespace-only boolean and numeric values.
Proposed fix- if (raw === undefined || raw === null || raw === '') return fallback
- if (typeof raw === 'boolean') return raw
- return String(raw).trim().toLowerCase() === 'true'
+ if (raw === undefined || raw === null) return fallback
+ const value = typeof raw === 'string' ? raw.trim() : raw
+ if (value === '') return fallback
+ if (typeof value === 'boolean') return value
+ return String(value).toLowerCase() === 'true'
...
- if (raw === undefined || raw === null || raw === '') return fallback
- const n = Number(raw)
+ if (raw === undefined || raw === null) return fallback
+ const value = typeof raw === 'string' ? raw.trim() : raw
+ if (value === '') return fallback
+ const n = Number(value)‼️ 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.
| const raw = config.get<string | boolean>(key) | |
| if (raw === undefined || raw === null || raw === '') return fallback | |
| if (typeof raw === 'boolean') return raw | |
| return String(raw).trim().toLowerCase() === 'true' | |
| } | |
| export function getNumber( | |
| config: ConfigService, | |
| key: string, | |
| fallback: number, | |
| ): number { | |
| const raw = config.get<string | number>(key) | |
| if (raw === undefined || raw === null || raw === '') return fallback | |
| const n = Number(raw) | |
| return Number.isFinite(n) ? n : fallback | |
| const raw = config.get<string | boolean>(key) | |
| if (raw === undefined || raw === null) return fallback | |
| const value = typeof raw === 'string' ? raw.trim() : raw | |
| if (value === '') return fallback | |
| if (typeof value === 'boolean') return value | |
| return String(value).toLowerCase() === 'true' | |
| } | |
| export function getNumber( | |
| config: ConfigService, | |
| key: string, | |
| fallback: number, | |
| ): number { | |
| const raw = config.get<string | number>(key) | |
| if (raw === undefined || raw === null) return fallback | |
| const value = typeof raw === 'string' ? raw.trim() : raw | |
| if (value === '') return fallback | |
| const n = Number(value) | |
| return Number.isFinite(n) ? n : fallback |
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api/src/common/config-coerce.ts` around lines 11 - 25, Update the boolean and numeric coercion helpers around getNumber and the adjacent boolean getter to trim string inputs before checking for emptiness, so whitespace-only values return fallback rather than being parsed. Preserve direct boolean handling and valid trimmed values, and add tests covering whitespace-only boolean and numeric configuration values.
Sorry, something went wrong.
| this.maxSmsBatchSize = getNumber( | ||
| this.configService, | ||
| 'MAX_SMS_BATCH_SIZE', | ||
| 100, | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Require a positive integer batch size.
At Line 20, 0, negative values, and fractional values pass getNumber. When fcmMessages is nonempty, the loop at Line 49 never completes for 0 or negative values. This can block the queue worker.
Validate MAX_SMS_BATCH_SIZE as a positive integer. Use 100 when validation fails.
Proposed fix- this.maxSmsBatchSize = getNumber(
+ const maxSmsBatchSize = getNumber(
this.configService,
'MAX_SMS_BATCH_SIZE',
100,
)
+ this.maxSmsBatchSize =
+ Number.isInteger(maxSmsBatchSize) && maxSmsBatchSize > 0
+ ? maxSmsBatchSize
+ : 100‼️ 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.
| this.maxSmsBatchSize = getNumber( | |
| this.configService, | |
| 'MAX_SMS_BATCH_SIZE', | |
| 100, | |
| ) | |
| const maxSmsBatchSize = getNumber( | |
| this.configService, | |
| 'MAX_SMS_BATCH_SIZE', | |
| 100, | |
| ) | |
| this.maxSmsBatchSize = | |
| Number.isInteger(maxSmsBatchSize) && maxSmsBatchSize > 0 | |
| ? maxSmsBatchSize | |
| : 100 |
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api/src/gateway/queue/sms-queue.service.ts` around lines 20 - 24, Update the MAX_SMS_BATCH_SIZE initialization in the SMS queue service constructor to accept only positive integer values; fall back to 100 when getNumber returns zero, a negative number, a fraction, or otherwise invalid input, while preserving valid configured batch sizes.
Sorry, something went wrong.
| if (firebaseConfig.projectId) { | ||
| firebase.initializeApp({ | ||
| credential: firebase.credential.cert(firebaseConfig), | ||
| }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -a 'package*.json' api -x rg -n '"firebase-admin"' {}
rg -n -C 3 'FIREBASE_(PROJECT_ID|PRIVATE_KEY|CLIENT_EMAIL)|credential\.cert|initializeApp' apiRepository: vernu/textbee
Length of output: 1451
🌐 Web query:
For firebase-admin version 13.6.0, what fields does firebase.credential.cert() require when passed a service-account object?
💡 Result:
For firebase-admin version 13.6.0, the firebase.credential.cert function accepts a service-account object defined by the ServiceAccount interface [1][2]. This object requires the following three fields [3][2]: projectId (string) clientEmail (string) privateKey (string) While earlier releases of the 13.x version series initially encountered issues where the SDK expected snake_case keys (such as client_email and private_key) instead of the camelCase properties defined in the interface [4][5], these issues were resolved in version 13.0.1 and later [4][5]. Therefore, in version 13.6.0, you should use the camelCase fields as defined in the TypeScript interface [3][2].
Citations:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- dependency declarations ---'
fd -a 'package*.json' . -x rg -n -C 2 '"firebase-admin"|"lockfileVersion"' {}
printf '%s\n' '--- Firebase configuration and initialization ---'
sed -n '50,82p' api/src/main.ts
printf '%s\n' '--- static completeness check ---'
python3 - <<'PY'
from pathlib import Path
import re
text = Path("api/src/main.ts").read_text()
config = re.search(r"const firebaseConfig = \{(.*?)\n \}", text, re.S).group(1)
guard = re.search(r"if \((.*?)\) \{", text[text.index("const firebaseConfig"):]).group(1)
fields = re.findall(r"^\s+(\w+):", config, re.M)
print("guard:", guard)
print("config fields:", fields)
print("required credential fields present in config:",
{name: name in fields for name in ("projectId", "clientEmail", "privateKey")})
print("guard checks all required fields:",
all(name in guard for name in ("projectId", "clientEmail", "privateKey")))
PYRepository: vernu/textbee
Length of output: 334
Validate all required Firebase credentials before initialization.
firebase.credential.cert() requires projectId, clientEmail, and privateKey. Check all three fields before calling firebase.initializeApp(). If any field is missing, skip Firebase initialization and log that the configuration is incomplete.
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api/src/main.ts` around lines 72 - 75, Update the Firebase initialization guard around firebase.initializeApp to require firebaseConfig.projectId, firebaseConfig.clientEmail, and firebaseConfig.privateKey before calling firebase.credential.cert(). When any credential is absent, skip initialization and log that the Firebase configuration is incomplete.
Sorry, something went wrong.
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Sorry, something went wrong.
…SReceivedWorker KEY_RETRY_COUNT was written as 0 and never incremented, so the retryCount >= MAX_RETRIES cap was dead code and status forwards could retry forever, including unrecoverable 401/404 responses. Switch to WorkManager's runAttemptCount, which is maintained across retries. Also catch JsonSyntaxException around the Retrofit response parse so a malformed 2xx body retries instead of escaping and marking the work permanently FAILED. Mirrors the existing fix in SMSStatusUpdateWorker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary
Three related fixes for the SMS pipeline — one API-side config bug and two Android WorkManager worker bugs.
1. Coerce boolean/numeric env vars for SMS queue config (432e617)
Env vars arrive as strings, so boolean/numeric queue-config values were being read as truthy strings / NaN. Coerced them to their real types so the SMS queue config behaves as intended.
2. Key SMS status work by smsId to stop collision drops (c59f2e4)
SMSStatusUpdateWorker built its unique-work name from status + currentTimeMillis() with ExistingWorkPolicy.REPLACE. Two reports in the same millisecond (bulk sends, multipart SMS) collided on that name and the pending one was cancelled before it was ever sent — a silently lost status report. Now keyed on (smsId, status) with ExistingWorkPolicy.KEEP, which also dedupes the N duplicate broadcasts from multipart messages.
3. Fix dead retry cap + narrow catch in SMS workers (e33f669)
Same pattern in both SMSStatusUpdateWorker and SMSReceivedWorker:
SMSStatusUpdateWorker already carried this fix; this brings SMSReceivedWorker in line so both workers behave identically.
Testing
🤖 Generated with Claude Code
Summary by CodeRabbit