Repository navigation
fix(translate): answer blank texts with themselves instead of failing the request - #245
Merged
Merged
Conversation
… the request A request fails as a whole when any one of its texts is empty, so a document split into lines cannot be translated at all if it has a blank line in it: ["", "Hello"] answers 404 "No text to translate" and nothing is translated. A blank text — empty, or nothing but whitespace — carries nothing to translate, so it is no longer sent, and its own text is the answer. That keeps the response position-aligned, and it is the only thing that can work: oneshot echoes an empty input, and the positional check would read that echo as a failed translation and answer 503 instead. A whitespace-only text survives today only because oneshot happens to echo it as well. The 404 is now only for a request with no text at all — no texts, or every text empty. A request that has text but nothing to translate, like [" "], is answered with its own texts, so no blank input errors any more and no input that used to succeed fails now. This matches the JavaScript client (@deeplx/core batchPayload and restoreBlankSegments), which already drops text.trim() === '' segments and puts them back, so the two sides agree on what a blank segment means. Refs OwO-Network#240, OwO-Network#241, OwO-Network#244.
…s well Every text is answered now, blank or not, so the all-empty case needs no rule of its own: "" and [""] are texts with nothing to translate in them, and they are answered with themselves like any other blank text. A missing or null `text` decodes to the one empty string and is answered the same way. The 404 is left for a request that names no text at all: `"text": []`, the one shape there is nothing to answer with. Nothing else errors on emptiness. Refs OwO-Network#244.
`"text": []` names a list that happens to be empty, and the response can
render that perfectly well, so rejecting it bought nothing: it was the
original "No text to translate" condition surviving for the one shape the
code had no answer to. It is answered with `{"data": []}` now — the same
rule as every other text, applied to none of them.
A body that never named `text` is a different thing, a payload without its
parameter: it answers 400 Invalid request payload instead of the 404 that
stood in for it, which is also what the endpoint answers for a body it
cannot bind. That is the only refusal left.
Refs OwO-Network#244.
JounQin
force-pushed
the
feat/blank-text-passthrough
branch
from
October 10, 2026 16:31
b86612e to
b28dcd4
Compare
UnmarshalJSON sent `null` down the string branch, where json.Unmarshal into a
string is a documented no-op, so `{"text":null}` decoded to `[""]` and was
indistinguishable from `{"text":""}` for the rest of the request. On main both
were an error, but this branch answers an empty text with itself, so null
quietly became a 200 instead.
A JSON null is a present key with no value, so it leaves the zero value now —
the same Texts == nil a body with no `text` key has — and the handler refuses
both with 400 Invalid request payload, testing the field directly instead of
re-deriving the decoder with `!Batch && len(Texts) == 0`. Every shape that
names text, including `"text": []`, still decodes to a non-nil Texts and is
answered rather than refused.
Refs OwO-Network#244.
JounQin
added a commit
to un-ts/deeplx
that referenced
this pull request
Oct 10, 2026
Nothing about emptiness is an error now, matching OwO-Network/DLX#245 rule for rule: a blank text (empty or whitespace-only) is answered with itself and is never sent, an empty array is answered with an empty array, and a blank string with that same string. Blank segments are not charged against the 1500-character cap either, because they do not travel. This replaces the empty-array and all-blank throws (and the 404 a blank string used to resolve to); the one failure left is a response that does not line up with the texts that were sent. The CLI no longer drops blank `--text` values, so a document split into lines keeps them (a blank `--file` path is still not a file), and the READMEs, the changeset and the tests follow. The core bundle grows to 3.12kB, so its size-limit budget is raised to 3.2kB.
JounQin
added a commit
to un-ts/deeplx
that referenced
this pull request
Oct 10, 2026
Verified against the merged implementation (OwO-Network/DLX#245, ee11142) rather than against its description alone, which closes three gaps: - A null or missing element *inside* a list is the empty string, as the Go decoder leaves it, instead of being refused; any other non-string element is still refused, exactly like the decoder that cannot bind it. - The languages are resolved before the cap, as the sibling does, so an unsupported language is a 400 even when the request is also over the limit. - The 413 message is the sibling's own sentence again — same code and same text for one text or for a list — rather than the batch wording and the unit suffix invented here; the README and the code comments still name the UTF-16 unit. A request that names no text (null, undefined, a wrong type, a list holding something that is not a string) now resolves to 400 Invalid request payload, the same payload error the sibling answers. normalizeTexts keeps the any of Array.isArray out of the validation, so a wrong value cannot reach trim or the request body as a crash.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #244. Nothing about emptiness is an error now: every text is answered — translated when it carries something, returned as it arrived when it is blank — and an empty list is answered with an empty list.
A request used to fail as a whole when any one of its texts was empty, so a document split into lines could not be translated at all if it had a blank line in it. The rule was inconsistent too —
""failed the whole request while whitespace-only passed through, because whitespace survived only as a side effect of oneshot echoing it back.["", "Hello"]404 No text to translate200 ["", "你好"]["Hello", ""]404200 ["你好", ""]["", " "]404200 ["", " "]" ",[" "]200, whitespace sent upstream200, answered locally"",[""],["", ""]404200, texts echoed back"text": []404 No text to translate200 {"data": []}— the request is mirrorednulltext404, then200 ""on this branch400 Invalid request payload— a null text names no texttextfield404 No text to translate400 Invalid request payloadA
nulltext is the same case as a missing one, and it used to be the opposite one.json.Unmarshalinto astringis a documented no-op fornull, so{"text":null}decoded to[""]and was indistinguishable from{"text":""}for the rest of the request:404onmain, and200 ""on this branch, which nobody asked for.UnmarshalJSONnow leaves the zero value fornull, soTexts == nilmeans "the request named no text" — the key is missing or null — and the handler tests exactly that instead of re-deriving the decoder with!Batch && len(Texts) == 0.The refusals left are therefore a body that never named
textand anullone: in both cases the request carries no text, which is a payload without its parameter rather than an empty text, and it is the same400the endpoint already answers for a body it cannot bind."text": []is not that case: it names a list that happens to be empty, and the response renders it perfectly well.Forwarding a blank cannot work: oneshot echoes an empty input, and the positional all-or-nothing check reads that echo as a failed translation, so the answer becomes
503 Translation failed. Measured live on83f97d3with only the empty-element guard removed,["", "Hello"]and["Hello", ""]both answer that 503. The bypass therefore happens here.Change
totalTextLength(texts)becomestextsToTranslate(texts) (positions []int, total int): a blank text is skipped and not charged against the cap, and the zero-text request leaves both empty.TranslateByDLXanswers every request: the texts that carry something travel upstream, andmergeTranslationssplices the answers back with every blank text left exactly as it arrived. When there is nothing to send, the same call returns the texts unchanged — allocated, so an empty request marshals as[]and notnull.PayloadText.UnmarshalJSONleaves its zero value for anulltext, soTexts == nilcovers both ways of naming no text, and the/translatehandler refuses that with400 Invalid request payloadbefore translating. Any other request mirrors the shape it was given.textsToTranslatepositions and length; puremergeTranslations;TranslateByDLXansweringnil,[],"",[""],["", ""], whitespace and["", " "]with themselves; decode tests pinningnulland the missing key to a nilTextsagainst"text": []'s allocated empty slice; handler-level tests for the"data":[]/"data":""shapes and the no-text400; and the cap cases on the reduced payload.This matches the JavaScript client (
@deeplx/corebatchPayloadandrestoreBlankSegments), which already dropstext.trim() === ''segments and restores them.go build ./...,go vet ./...andgo test ./...pass. Live on a build of this branch:["", "Hello"],["Hello", ""],["", " "],[" "],[" ", "Hello"],"",[""],["", ""]and{"text":[]}all answer200with data in the shape the caller used; a body with notext, or"text": null, answers400 Invalid request payload;["", "😀"×751]answers413 text exceeds maximum length: 1502 charactersand["😀"×400, "", "😀"×400]answers413 … 1600 characters, so blanks are not charged against the cap.Refs #240, #241.