{"record":{"id":"1eb3fee2f039158d","repo":"floci-io/floci","slug":"badrequestexception","errorCode":"BadRequestException","errorMessage":"Reserved tag key ${key} can only be supplied during resource creation.","messagePattern":"Reserved tag key (.+?) can only be supplied during resource creation\\.","errorType":"http","errorClass":"AwsException","httpStatus":400,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/core/common/ReservedTags.java","lineNumber":98,"sourceCode":"     */\n    public static Map<String, String> stripApiGatewayReservedTags(Map<String, String> tags) {\n        Map<String, String> stripped = stripReservedTags(tags);\n        stripped.remove(DEPRECATED_API_GATEWAY_CUSTOM_ID_KEY);\n        return stripped;\n    }\n\n    /**\n     * API Gateway update-path guard. An id override only makes sense at create time, so both the\n     * reserved keys and the deprecated custom-id key are rejected here, with API Gateway's declared\n     * error code rather than the {@code ValidationException} the shared guard uses.\n     */\n    public static void rejectApiGatewayReservedTagsOnUpdate(Map<String, String> tags) {\n        if (tags == null) {\n            return;\n        }\n        for (String key : tags.keySet()) {\n            if (isReserved(key) || DEPRECATED_API_GATEWAY_CUSTOM_ID_KEY.equals(key)) {\n                throw new AwsException(\n                        BAD_REQUEST_EXCEPTION,\n                        \"Reserved tag key \" + key + \" can only be supplied during resource creation.\",\n                        400\n                );\n            }\n        }\n    }\n\n    public static void rejectReservedTagsOnUpdate(Map<String, String> tags) {\n        if (tags == null) {\n            return;\n        }\n        for (String key : tags.keySet()) {\n            if (isReserved(key)) {\n                throw new AwsException(\n                        VALIDATION_EXCEPTION,\n                        \"Reserved tag keys with prefix \" + RESERVED_PREFIX + \" can only be supplied during resource creation.\",\n                        400","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/core/common/ReservedTags.java#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Guard PutJobSuccessResult/PutJobFailureResult with a try-catch for InvalidJobStateException and treat it as already-reported","Report exactly one terminal result per jobId — move the success call out of finally blocks so it cannot run after a failure report","Check GetJobDetails status before retrying a completion call"],"exampleFix":"// before\ntry { runWork(); client.putJobSuccessResult(...); } catch (Exception e) { client.putJobFailureResult(...); } finally { client.putJobSuccessResult(...); }\n\n// after\ntry {\n    runWork();\n    client.putJobSuccessResult(...);\n} catch (Exception e) {\n    client.putJobFailureResult(r -> r.jobId(jobId).failureDetails(...));\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    client.putJobSuccessResult(r -> r.jobId(jobId).executionDetails(...));\n} catch (InvalidJobStateException alreadyDone) {\n    logger.warn(\"job {} already completed; skipping duplicate result\", jobId);\n}","preventionTips":["Submit exactly one terminal result per job; never put success reporting in a finally block","Check job status via GetJobDetails before resubmitting a result after a crash"],"tags":["codepipeline","job-completion","idempotency","job-state"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}