You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three of CloudWatch Logs' four paginating operations publish ResourceNotFoundException for a log
group that does not exist. Substrate answers HTTP 200 with an empty listing at all three, so a
caller cannot tell "this group is empty" from "this group was never created" — and a consumer whose
error path exists to handle a typo'd or not-yet-created log group never reaches it.
What the pages publish
API_DescribeLogStreams, API_GetLogEvents and API_FilterLogEvents each publish, in their own
Errors sections:
ResourceNotFoundException
The specified resource does not exist.
HTTP Status Code: 400
The status is 400, not 404, and that is the page's own figure — it reads like a typo and is not. docs/services.md's CloudWatch Logs section already records this for PutRetentionPolicy and notes
that "Substrate's older group- and stream-level not-found responses on the other operations still use
404 and are not changed here", so the plugin is inconsistent with itself as well as with the pages.
API_DescribeLogGroups publishes noResourceNotFoundException — its Errors section is InvalidParameterException/400 and ServiceUnavailableException/500 only — which is right, because a
listing with a prefix that matches nothing is legitimately empty. So this is a three-site issue, not
four.
What substrate does
describeLogStreams checks that logGroupName is present, then reads the stream-names index
directly. A group that does not exist has no index, so loadStringIndex returns nil, nil and the
operation answers {"logStreams":[]}.
getLogEvents checks both required members, then reads the events key. No group record and no stream
record is consulted, so a nonexistent group or a nonexistent stream answers {"events":[]}.
filterLogEvents is the same shape via the stream index.
putLogEvents does resolve both, and answers ResourceNotFoundException at 404 — so the code
exists in the plugin, at the wrong status, and is simply not reached from the three read paths.
Why it matters beyond the missing code
Found while converting these four operations' pagination tokens for #1086 (see emulator/cloudwatchlogs_pagination.go, which records this as out of that change's scope). The
ordering interacts: #1086 established that the token is decoded before any state is read, and GetLogEvents also requires logStreamName. Adding a resource resolution puts a third refusal in the
sequence, and AWS publishes nothing about which of a bad token and a missing resource wins. The
in-tree precedent is SNS ListSubscriptionsByTopic (#926), where the NotFound keeps precedence
because the topic is the resource the request addresses and the token is a continuation of a listing
over it — the same argument applies to a log group, and the seal in pagination_token_refusal_test.go is already written to accommodate it (sealGetKey).
Acceptance criteria
DescribeLogStreams, GetLogEvents and FilterLogEvents answer ResourceNotFoundException at HTTP 400 for a logGroupName that has no record, with a message naming the group.
GetLogEvents answers the same for a logStreamName that has no record under an existing group,
matching putLogEvents' existing distinction between the two.
DescribeLogGroups is unchanged — a prefix matching nothing stays an empty 200, because its
page publishes no such code.
The existing 404s elsewhere in the plugin are either moved to 400 in the same change or the split
is recorded with the reason; the current docs/services.md sentence saying they "are not changed
here" must stop being the whole of the explanation.
Three of CloudWatch Logs' four paginating operations publish
ResourceNotFoundExceptionfor a loggroup that does not exist. Substrate answers HTTP 200 with an empty listing at all three, so a
caller cannot tell "this group is empty" from "this group was never created" — and a consumer whose
error path exists to handle a typo'd or not-yet-created log group never reaches it.
What the pages publish
API_DescribeLogStreams,API_GetLogEventsandAPI_FilterLogEventseach publish, in their ownErrors sections:
The status is 400, not 404, and that is the page's own figure — it reads like a typo and is not.
docs/services.md's CloudWatch Logs section already records this forPutRetentionPolicyand notesthat "Substrate's older group- and stream-level not-found responses on the other operations still use
404 and are not changed here", so the plugin is inconsistent with itself as well as with the pages.
API_DescribeLogGroupspublishes noResourceNotFoundException— its Errors section isInvalidParameterException/400 andServiceUnavailableException/500 only — which is right, because alisting with a prefix that matches nothing is legitimately empty. So this is a three-site issue, not
four.
What substrate does
describeLogStreamschecks thatlogGroupNameis present, then reads the stream-names indexdirectly. A group that does not exist has no index, so
loadStringIndexreturnsnil, niland theoperation answers
{"logStreams":[]}.getLogEventschecks both required members, then reads the events key. No group record and no streamrecord is consulted, so a nonexistent group or a nonexistent stream answers
{"events":[]}.filterLogEventsis the same shape via the stream index.putLogEventsdoes resolve both, and answersResourceNotFoundExceptionat 404 — so the codeexists in the plugin, at the wrong status, and is simply not reached from the three read paths.
Why it matters beyond the missing code
Found while converting these four operations' pagination tokens for #1086 (see
emulator/cloudwatchlogs_pagination.go, which records this as out of that change's scope). Theordering interacts: #1086 established that the token is decoded before any state is read, and
GetLogEventsalso requireslogStreamName. Adding a resource resolution puts a third refusal in thesequence, and AWS publishes nothing about which of a bad token and a missing resource wins. The
in-tree precedent is SNS
ListSubscriptionsByTopic(#926), where theNotFoundkeeps precedencebecause the topic is the resource the request addresses and the token is a continuation of a listing
over it — the same argument applies to a log group, and the seal in
pagination_token_refusal_test.gois already written to accommodate it (sealGetKey).Acceptance criteria
DescribeLogStreams,GetLogEventsandFilterLogEventsanswerResourceNotFoundExceptionatHTTP 400 for a
logGroupNamethat has no record, with a message naming the group.GetLogEventsanswers the same for alogStreamNamethat has no record under an existing group,matching
putLogEvents' existing distinction between the two.DescribeLogGroupsis unchanged — a prefix matching nothing stays an empty 200, because itspage publishes no such code.
is recorded with the reason; the current
docs/services.mdsentence saying they "are not changedhere" must stop being the whole of the explanation.
ListSubscriptionsByTopic,recorded in the plugin, and asserted — including that the token is still refused before any state
is read when the resource does exist.
since the status is the part that is wrong twice over.
docs/services.mddrops this from the list of divergences Fifteen offset-token sites still answer page one for a token substrate never issued #1086 left in place.