Skip to content

Conversation

@macalbert
Copy link
Owner

@macalbert macalbert commented Jan 26, 2026

Pull Request

What does this PR do?

This PR significantly improves the robustness and reliability of the push operation in Envilder by addressing multiple error handling and operational issues:

  1. AWS Throttling Resilience: Implements exponential backoff with jitter to handle AWS SSM throttling errors (TooManyUpdates, ThrottlingException) automatically
  2. Duplicate Path Validation: Validates that multiple environment variables mapping to the same SSM path have consistent values, preventing silent data corruption
  3. Enhanced Error Logging: Provides comprehensive error messages for all error types (Error objects, AWS SDK errors, strings, undefined, etc.)
  4. Optimized SSM Operations: Groups parameters by SSM path to push each unique path only once, improving efficiency and reducing API calls
  5. Code Quality: Minor formatting fixes in CI workflow files

Related issues

Fixes issues with push command failures when encountering AWS throttling or duplicate path configurations.

Type of change

  • Bugfix
  • Feature
  • Refactor

Checklist

  • Tests added/updated (if needed) - Added 10+ new test cases covering retry logic, duplicate paths, and error handling scenarios
  • Docs updated (if needed) - N/A (internal implementation changes)
  • Lint/format pass - Includes style fixes for CI workflows

Notes for reviewer

Key Changes by Commit:

  1. 9855fe1: Style improvements in workflow files (quotes standardization)
  2. 61d4fc2: Initial error handling improvements and better error message formatting
  3. c5384d6: Core feature - retry logic with exponential backoff for AWS throttling
  4. d55d416: Validation logic to detect conflicting values for duplicate SSM paths
  5. 3ee3bbf: Code cleanup - removed unused parameter

Testing Coverage:

The PR includes comprehensive test coverage for:

  • Retry behavior with temporary throttling errors
  • Successful retry after failures
  • Validation of duplicate paths with conflicting values
  • Validation of duplicate paths with matching values (deduplication)
  • Error logging for all error types (Error, AWS SDK, string, undefined, objects)
  • Parallel processing with partial failures

Architecture Notes:

This follows the established Command/Handler pattern in the application layer. The changes are isolated to PushEnvToSsmCommandHandler and its tests, maintaining the hexagonal architecture boundaries (no changes to domain or infrastructure layers).

Summary by CodeRabbit

  • New Features

    • Automatic retry with exponential backoff and jitter for transient throttling errors.
  • Improvements

    • Batch-style processing when pushing environment values to parameters.
    • Validation to detect conflicting mappings to the same parameter and surface masked conflicts.
    • Enhanced logging and normalized error messages for clearer diagnostics.
    • Parallelized parameter pushes with per-parameter error handling.
  • Documentation

    • Standardized YAML string quoting in docs.
  • Tests

    • Expanded coverage: retries, error shapes, parallel processing, conflict scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

Marçal Albert Castellví added 5 commits January 26, 2026 23:13
…ndHandler

Implement exponential backoff with jitter for AWS SSM throttling errors.
Enhance error logging to provide more context on failures.
Add tests to verify retry behavior and error handling.
- Refactored the logic to validate and group environment variables
  by their SSM paths, ensuring consistent values for duplicate paths.
- Updated the push logic to handle grouped parameters, improving
  clarity and maintainability of the code.
- Enhanced error handling for conflicting values.
@macalbert macalbert requested a review from Copilot January 26, 2026 23:38
@macalbert macalbert self-assigned this Jan 26, 2026
@macalbert macalbert added the enhancement New feature or request label Jan 26, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @macalbert, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the reliability and robustness of the push operation within Envilder. It introduces mechanisms to gracefully handle AWS SSM throttling, prevents potential data inconsistencies by validating duplicate SSM path mappings, and optimizes SSM API interactions. The changes aim to provide a more stable and predictable experience when pushing environment variables to AWS SSM, ensuring data integrity and operational resilience.

Highlights

  • AWS Throttling Resilience: Implemented exponential backoff with jitter to automatically handle AWS SSM throttling errors like "TooManyUpdates" and "ThrottlingException".
  • Duplicate Path Validation: Added validation to ensure environment variables mapping to the same SSM path have consistent values, preventing potential data corruption.
  • Optimized SSM Operations: Parameters are now grouped by unique SSM paths, reducing redundant API calls and improving efficiency.
  • Enhanced Error Logging: Improved error message formatting to provide comprehensive details for various error types, including AWS SDK errors, strings, and undefined values.
  • Code Quality Improvements: Applied minor formatting fixes to CI workflow files for consistency.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: .github/workflows/** (5)
    • .github/workflows/coverage-report.yml
    • .github/workflows/publish-action.yml
    • .github/workflows/test-action.yml
    • .github/workflows/tests.yml
    • .github/workflows/verify-action-build.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Walkthrough

Documentation YAML string literals standardized; PushEnvToSsm handler refactored to validate and group environment variables by SSM path, perform batch parallel pushes with exponential-backoff retry and normalized error handling; tests expanded for validation, retries, parallel processing, and diverse error cases.

Changes

Cohort / File(s) Summary
Documentation
docs/github-action.md
Standardized YAML examples: replaced single-quoted string literals with double-quoted literals for values like node-version and cache.
Handler Implementation
src/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.ts
Reworked flow to: loadConfigurationvalidateAndGroupByPathpushParametersToSSM. Added grouping map of SSM path → {value, sourceKeys}, conflict validation (throws on differing values), pushParameter, pushParametersToSSM, retryWithBackoff (exponential backoff + jitter for TooManyUpdates/Throttling/TooManyRequests), and getErrorMessage. Switched from per-variable sequential pushes to batched/parallel path processing and expanded logging.
Test Suite
tests/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.test.ts
Added comprehensive tests: skip missing envs, conflict detection for same SSM path with differing values, dedupe identical-values, success logging, handling of non-Error/string/undefined/AWS-style errors, retry behavior (throttling and AWS SDK-like), partial failures with parallel attempts, and assertions for setParameter/setSecret call counts and error propagation.

Sequence Diagram

sequenceDiagram
    actor Handler as PushEnvToSsmCommandHandler
    participant Config as Configuration
    participant Validation as Validation & Grouping
    participant Retry as Retry Logic
    participant SSM as AWS SSM

    Handler->>Config: loadConfiguration()
    Config-->>Handler: config

    Handler->>Validation: validateAndGroupByPath(envVariables, paramMap)
    Validation->>Validation: build Map<ssmPath, {value, sourceKeys}>
    alt Conflict detected
        Validation-->>Handler: throw Error
    else Valid map
        Validation-->>Handler: pathToValueMap
    end

    loop For each unique SSM path (parallel)
        Handler->>Retry: pushParameter(path, value, sourceKeys)
        Retry->>SSM: PutParameter
        alt Throttling-like error
            Retry->>Retry: exponential backoff + jitter
            Retry->>SSM: retry PutParameter
        else Success
            SSM-->>Retry: OK
        end
        Retry-->>Handler: result / error
    end

    Handler->>Handler: log summary (total vars, unique paths, failures)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main features: AWS throttling retry logic and duplicate path validation, matching the core changes in the PR.
Description check ✅ Passed The description comprehensively covers all required sections: what the PR does (5 detailed points), related issues, type of change (3 options selected), complete checklist, and detailed notes for reviewers.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the robustness of Envilder's push operation by adding AWS throttling retry logic with exponential backoff and duplicate path validation to prevent data corruption.

Changes:

  • Implements retry logic with exponential backoff + jitter for AWS SSM throttling errors (TooManyUpdates, ThrottlingException)
  • Adds validation to detect conflicting values when multiple environment variables map to the same SSM path
  • Groups parameters by SSM path to push each unique path only once, reducing API calls
  • Enhances error logging to handle all error types comprehensively

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.ts Core implementation of retry logic, duplicate path validation, parameter grouping optimization, and enhanced error handling
tests/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.test.ts Comprehensive test coverage for retry behavior, duplicate path validation, and error handling scenarios
github-action/dist/index.js Bundled GitHub Action with new implementation
e2e/cli.test.ts E2E test for successful push operation
.github/workflows/*.yml Style formatting improvements (quote standardization, trailing space removal)
docs/github-action.md, github-action/README.md Documentation style formatting improvements

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly improves the robustness of the push operation by introducing exponential backoff for AWS throttling and validation for duplicate SSM paths. However, a security audit identified critical issues related to sensitive data handling in error paths. Specifically, secret values are explicitly included in error messages when conflicts are detected, and the general error logging mechanism stringifies entire error objects, which may contain secrets, risking accidental leakage into application logs. Additionally, consider restoring a helpful warning for missing environment variables to improve user experience.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.ts`:
- Around line 94-99: The error thrown in PushEnvToSsmCommandHandler exposes raw
secret values (existing.value and envValue); change the error message to use
masked values via EnvironmentVariable.maskedValue(...) for both existing.value
and envValue (keep ssmPath and source keys as-is), e.g., call
EnvironmentVariable.maskedValue(existing.value) and
EnvironmentVariable.maskedValue(envValue) when composing the message to avoid
leaking secrets; ensure no other logs or thrown messages in this handler include
the raw values.
🧹 Nitpick comments (3)
src/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.ts (2)

116-134: Consider Promise.allSettled for comprehensive error reporting.

Using Promise.all causes the handler to fail fast on the first error, potentially hiding other failures. If comprehensive error reporting is desired (e.g., showing all failed parameters), consider Promise.allSettled.

However, the current fail-fast behavior is consistent with the existing error handling pattern and may be intentional.


152-193: Consider logging retry attempts for observability.

The retry logic is correct, but retries happen silently. Adding a debug/info log when retrying would improve observability and help diagnose throttling issues in production.

📊 Proposed fix to add retry logging
         // Calculate delay with exponential backoff + jitter
         const exponentialDelay = baseDelayMs * 2 ** attempt;
         const jitter = Math.random() * exponentialDelay * 0.5; // 0-50% jitter
         const delayMs = exponentialDelay + jitter;

+        this.logger.info(
+          `Throttled by AWS SSM, retrying in ${Math.round(delayMs)}ms (attempt ${attempt + 1}/${maxRetries})`,
+        );
+
         await new Promise((resolve) => setTimeout(resolve, delayMs));
tests/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.test.ts (1)

82-106: Test name could be more descriptive.

The test name Should_Warning_When_ImportsNotMatchesKey is misleading since no warning is logged. The test verifies that missing variables are skipped silently. Consider renaming for clarity.

✏️ Suggested rename
-  it('Should_Warning_When_ImportsNotMatchesKey', async () => {
+  it('Should_SkipMissingVariable_When_EnvVariableNotInEnvFile', async () => {

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.test.ts (1)

82-106: Test name has grammatical issue and is missing warning assertion.

The test name Should_Warning_When_ImportsNotMatchesKey should follow proper grammar (e.g., Should_LogWarning_When_EnvironmentVariableNotFound). Additionally, the test verifies only that setSecret was called once but doesn't assert that logger.warn was called for the missing variable, even though the test name implies warning behavior is being tested.

💡 Proposed fix
- it('Should_Warning_When_ImportsNotMatchesKey', async () => {
+ it('Should_LogWarningAndSkipVariable_When_EnvironmentVariableNotFound', async () => {
    // Arrange
    mockVariableStore.getMapping.mockResolvedValue({
      TEST_ENV_VAR: '/path/to/ssm/test',
      MISSING_VAR: '/path/to/ssm/missing',
    });

    mockVariableStore.getEnvironment.mockResolvedValue({
      TEST_ENV_VAR: 'test-value',
      // MISSING_VAR is not present
    });

    const command = PushEnvToSsmCommand.create(mockMapPath, mockEnvFilePath);

    // Act
    await sut.handle(command);

    // Assert
    // Only TEST_ENV_VAR should be pushed (MISSING_VAR is skipped)
    expect(mockSecretProvider.setSecret).toHaveBeenCalledTimes(1);
    expect(mockSecretProvider.setSecret).toHaveBeenCalledWith(
      '/path/to/ssm/test',
      'test-value',
    );
+   expect(mockLogger.warn).toHaveBeenCalledWith(
+     expect.stringContaining('MISSING_VAR'),
+   );
  });
🧹 Nitpick comments (3)
tests/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.test.ts (1)

176-181: Redundant assertion can be simplified.

The second assertion on lines 179-181 is redundant since if mockLogger.error was never called (line 178), it couldn't have been called with any specific argument. Consider removing the redundant check.

♻️ Proposed simplification
    // Assert
    // Should NOT show any "Failed..." error message
    expect(mockLogger.error).not.toHaveBeenCalled();
-   expect(mockLogger.error).not.toHaveBeenCalledWith(
-     expect.stringContaining('Failed to push environment file'),
-   );
    // Should show success message
src/envilder/application/pushEnvToSsm/PushEnvToSsmCommandHandler.ts (2)

94-123: Past review addressed: secrets are now properly masked.

The conflicting values error message now uses EnvironmentVariable.maskedValue instead of raw values.

However, totalVariables on line 117 counts all mappings including those where the environment variable was not found, making the log message potentially misleading when variables are skipped.

,

♻️ Optional: More accurate logging
+   let validatedCount = 0;
    for (const [envKey, ssmPath] of Object.entries(paramMap)) {
      const envValue = envVariables[envKey];

      if (envValue === undefined) {
        this.logger.warn(
          `Warning: Environment variable ${envKey} not found in environment file`,
        );
        continue;
      }
+     validatedCount++;

      // ... rest of the loop
    }

    const uniquePaths = pathToValueMap.size;
-   const totalVariables = Object.keys(paramMap).length;
    this.logger.info(
-     `Validated ${totalVariables} environment variables mapping to ${uniquePaths} unique SSM parameters`,
+     `Validated ${validatedCount} environment variables mapping to ${uniquePaths} unique SSM parameters`,
    );

125-143: Parallel processing with Promise.all may mask individual failures.

Using Promise.all means if multiple parameters fail, only the first rejection is surfaced. Consider whether Promise.allSettled followed by error aggregation would provide better visibility into all failures.

For now this is acceptable since the test confirms all parameters are attempted, but worth considering for future improvement if detailed error reporting per parameter becomes needed.

@macalbert macalbert merged commit 16c5add into main Jan 27, 2026
9 checks passed
@macalbert macalbert deleted the macalbert/fix-push-error-handling branch January 27, 2026 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants