floci-io/floci · error · AwsException

BadRequestException

BadRequestException

Error message

Reserved tag key ${key} can only be supplied during resource creation.

What it means

Thrown by the emulator's job-completion handler (backing PutJobSuccessResult and PutJobFailureResult) when the referenced job's status is neither 'Created' nor 'InProgress'. A job that already reported Succeeded or Failed cannot be completed again; every completion call must target a live job. The nonce-free lookup is by jobId only, then the state guard fires.

Source

Thrown at src/main/java/io/github/hectorvent/floci/core/common/ReservedTags.java:98

     */
    public static Map<String, String> stripApiGatewayReservedTags(Map<String, String> tags) {
        Map<String, String> stripped = stripReservedTags(tags);
        stripped.remove(DEPRECATED_API_GATEWAY_CUSTOM_ID_KEY);
        return stripped;
    }

    /**
     * API Gateway update-path guard. An id override only makes sense at create time, so both the
     * reserved keys and the deprecated custom-id key are rejected here, with API Gateway's declared
     * error code rather than the {@code ValidationException} the shared guard uses.
     */
    public static void rejectApiGatewayReservedTagsOnUpdate(Map<String, String> tags) {
        if (tags == null) {
            return;
        }
        for (String key : tags.keySet()) {
            if (isReserved(key) || DEPRECATED_API_GATEWAY_CUSTOM_ID_KEY.equals(key)) {
                throw new AwsException(
                        BAD_REQUEST_EXCEPTION,
                        "Reserved tag key " + key + " can only be supplied during resource creation.",
                        400
                );
            }
        }
    }

    public static void rejectReservedTagsOnUpdate(Map<String, String> tags) {
        if (tags == null) {
            return;
        }
        for (String key : tags.keySet()) {
            if (isReserved(key)) {
                throw new AwsException(
                        VALIDATION_EXCEPTION,
                        "Reserved tag keys with prefix " + RESERVED_PREFIX + " can only be supplied during resource creation.",
                        400

View on GitHub (pinned to 62ff490619)

Solutions

  1. Guard PutJobSuccessResult/PutJobFailureResult with a try-catch for InvalidJobStateException and treat it as already-reported
  2. Report exactly one terminal result per jobId — move the success call out of finally blocks so it cannot run after a failure report
  3. Check GetJobDetails status before retrying a completion call

Example fix

// before
try { runWork(); client.putJobSuccessResult(...); } catch (Exception e) { client.putJobFailureResult(...); } finally { client.putJobSuccessResult(...); }

// after
try {
    runWork();
    client.putJobSuccessResult(...);
} catch (Exception e) {
    client.putJobFailureResult(r -> r.jobId(jobId).failureDetails(...));
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    client.putJobSuccessResult(r -> r.jobId(jobId).executionDetails(...));
} catch (InvalidJobStateException alreadyDone) {
    logger.warn("job {} already completed; skipping duplicate result", jobId);
}

Prevention

When it happens

Trigger: Calling PutJobSuccessResult after PutJobFailureResult for the same jobId (or vice versa); retrying a completion that already succeeded; completing a job whose pipeline execution was superseded and the job finalized.

Common situations: Worker retry logic that resubmits results after a timeout; long-running workers that finish after a newer execution already closed the job; bugs that submit both success and failure paths (e.g. success in a finally block after an exception already reported failure).

Understand the failure class

Background: BadRequestException (HTTP 400) — NestJS 'Bad Request' Errors: Why They Fire and How to Fix Them — this error's family across 4 libraries.

Related errors


AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14). Data as JSON: /api/errors/1eb3fee2f039158d. Report an issue: GitHub.