<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Known Good</title>
  <subtitle>Field notes from real fixes. Root cause first, then the fix, then the evidence.</subtitle>
  <link href="https://www.misc2.com/technotes/"/>
  <link rel="self" href="https://www.misc2.com/technotes/feed.xml"/>
  <id>https://www.misc2.com/technotes/</id>
  <updated>2026-10-09T00:00:00Z</updated>
  <author><name>Shaun</name></author>
  <entry>
    <title>Vertical Japanese (tategaki) OCR benchmark: dots.ocr-1.5 vs LightOnOCR-3 vs chandra-ocr-2</title>
    <link href="https://www.misc2.com/technotes/vertical-japanese-ocr-benchmark-dots-ocr-lightonocr-3-chandra/"/>
    <id>https://www.misc2.com/technotes/vertical-japanese-ocr-benchmark-dots-ocr-lightonocr-3-chandra/</id>
    <published>2026-10-09T00:00:00Z</published>
    <updated>2026-10-09T00:00:00Z</updated>
    <summary>On 33 vertical Japanese pages (21k chars, exact ground truth), dots.ocr-1.5 had 1.27% CER, LightOnOCR-3-4B 2.59%, chandra-ocr-2 10.96% (repetition loops; turns small kana っ into つ). LightOnOCR-3-1B ships mis-keyed weights and 0.8B ships use_cache=false.</summary>
    <category term="ocr"/><category term="japanese"/><category term="vlm"/><category term="benchmark"/><category term="transformers"/><category term="cuda"/><category term="python"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The question was which local OCR model to use for &lt;strong&gt;vertical Japanese&lt;/strong&gt; (縦書き, tategaki):
novels, newspapers, old books. Two things made it hard to answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easy benchmarks are saturated.&lt;/strong&gt; On clean English, tables and horizontal Japanese,
every recent OCR VLM scores 0.0–0.6% character error rate (CER). A single vertical
paragraph separated them by only 1–3 characters, which is noise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The failures that matter are not visible in averages of short pages.&lt;/strong&gt; On longer
vertical pages some models loop (repeat a phrase until the token limit), transcribe
bleed-through from the back of the page, or silently change the spelling system
(っ → つ, 來 → 来). These only show up with enough text and enough page types.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So a dedicated vertical-Japanese test set was built, with exact ground truth, and five
models were run on it.&lt;/p&gt;
&lt;p&gt;Results (CER = edits ÷ ground-truth characters; lower is better):&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;CER&lt;/th&gt;
&lt;th&gt;Median page&lt;/th&gt;
&lt;th&gt;Pages &amp;gt; 10% CER&lt;/th&gt;
&lt;th&gt;Time, 33 pages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;dots.ocr-1.5&lt;/strong&gt; (3B)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.27%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.96%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;271 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LightOnOCR-3-4B&lt;/td&gt;
&lt;td&gt;2.59%&lt;/td&gt;
&lt;td&gt;1.52%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;222 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LightOnOCR-3-0.8B&lt;/td&gt;
&lt;td&gt;4.15%&lt;/td&gt;
&lt;td&gt;3.48%&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;154 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LightOnOCR-3-1B&lt;/td&gt;
&lt;td&gt;9.00%&lt;/td&gt;
&lt;td&gt;4.41%&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;194 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chandra-ocr-2&lt;/td&gt;
&lt;td&gt;10.96% (90.6% uncapped)&lt;/td&gt;
&lt;td&gt;1.74%&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;629 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Each page&#x27;s edits are capped at its own length, so one runaway loop costs at most one
page of error. Without the cap, chandra&#x27;s single 18,500-character loop dominates
everything (90.6%).&lt;/p&gt;
&lt;p&gt;By page type (same metric):&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Pages&lt;/th&gt;
&lt;th&gt;LO3-4B&lt;/th&gt;
&lt;th&gt;LO3-0.8B&lt;/th&gt;
&lt;th&gt;LO3-1B&lt;/th&gt;
&lt;th&gt;chandra&lt;/th&gt;
&lt;th&gt;dots&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;clean block, 6 fonts&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1.51&lt;/td&gt;
&lt;td&gt;3.02&lt;/td&gt;
&lt;td&gt;8.63&lt;/td&gt;
&lt;td&gt;1.51&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.88&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;paperback page&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1.16&lt;/td&gt;
&lt;td&gt;2.39&lt;/td&gt;
&lt;td&gt;7.36&lt;/td&gt;
&lt;td&gt;1.76&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.92&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;furigana (ruby)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3.40&lt;/td&gt;
&lt;td&gt;3.78&lt;/td&gt;
&lt;td&gt;30.46&lt;/td&gt;
&lt;td&gt;9.24&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.29&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old orthography 旧字旧仮名&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3.68&lt;/td&gt;
&lt;td&gt;6.27&lt;/td&gt;
&lt;td&gt;6.75&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.70&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.71&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;numbers / Latin&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2.85&lt;/td&gt;
&lt;td&gt;0.57&lt;/td&gt;
&lt;td&gt;33.67&lt;/td&gt;
&lt;td&gt;3.71&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.14&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2–3 tier newspaper 段組&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2.99&lt;/td&gt;
&lt;td&gt;4.72&lt;/td&gt;
&lt;td&gt;2.93&lt;/td&gt;
&lt;td&gt;22.08&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.49&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;brush / pen fonts&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1.47&lt;/td&gt;
&lt;td&gt;2.94&lt;/td&gt;
&lt;td&gt;3.67&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.92&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;simulated scans&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2.28&lt;/td&gt;
&lt;td&gt;4.82&lt;/td&gt;
&lt;td&gt;8.80&lt;/td&gt;
&lt;td&gt;8.58&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.18&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;vertical-ja-sample-pages.jpg&quot; alt=&quot;Six of the 33 test pages: paperback, furigana, old orthography, 3-tier newspaper, numbers and Latin, simulated phone photo&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The differences come from model behaviour, not from reading order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reading order is fine for all of them.&lt;/strong&gt; The score was also computed order-free (a
multiset comparison of characters). The gap between ordered and order-free error is only
0.25–1.2 points for every model. All five read right-to-left columns and multi-tier
newspaper pages in the right sequence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;chandra-ocr-2 normalizes modern text to pre-war spelling.&lt;/strong&gt; It enlarges small kana
in modern text: っ → つ, ゃ → や, ょ → よ (123 times across the set), and sometimes
writes old kanji forms (気 → 氣). Example from a paperback page:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;ground truth: 糊代りに使った飯粒の / 貼らさってあった / な筈がなかった
chandra:      糊代りに使つた飯粒の / 貼らさつてあつた / な筈がなかつた
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same bias makes it the best model on genuinely old-orthography pages (0.70%).
It also produced three blowups: a repetition loop on a 2-tier page
(&lt;code&gt;…だっただっただった…&lt;/code&gt; for 18,546 characters, 233 s), mirrored bleed-through
transcribed as text on a thin-paper scan, and furigana emitted inline as
&lt;code&gt;&amp;lt;sup&amp;gt;reading&amp;lt;/sup&amp;gt;&lt;/code&gt;, sometimes attached to the wrong kanji.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LightOnOCR-3 does the opposite: it modernizes old orthography.&lt;/strong&gt; On 旧字旧仮名
pages the 4B writes 來 → 来, 樂 → 楽, 雜 → 雑, あつた → あった, and even rewrites
classical grammar (かへらば → かへれば). It also sometimes reformats prose into
Markdown (a recipe&#x27;s 、-separated ingredient list became &lt;code&gt;-&lt;/code&gt; bullets, plus an invented
「作り方」 heading). The 0.8B drops dakuten (ござ → ごさ) and leaks simplified Chinese
(済 → 济). The 1B is not usable for vertical text, even after the checkpoint repair
described below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dots.ocr-1.5 has the least spelling bias.&lt;/strong&gt; Its errors are isolated
misreadings (檸檬 → 檜櫟 in one font, ゐ → る on old text). It had no page above 2.7% CER.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Furigana:&lt;/strong&gt; if transcribed readings are forgiven, the ruby category is dots 0.91%,
LightOnOCR-3-4B 1.87%, chandra 2.54%. dots and the 4B mostly drop the ruby, as wanted;
the 4B occasionally writes it inline as 扇（あお）.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Use dots.ocr-1.5 for vertical Japanese.&lt;/strong&gt; It was the most accurate in 6 of 8
categories and never failed badly. LightOnOCR-3-4B is the next choice: zero blowups and
a bit faster, but expect modernized spelling on pre-war text. It also needs about 9 GB
of VRAM in bf16, versus about 6 GB for dots. Avoid chandra-ocr-2 for tategaki, or at
least guard it with a max-length check for loops.&lt;/p&gt;
&lt;p&gt;If you run &lt;strong&gt;LightOnOCR-3&lt;/strong&gt; with Hugging Face transformers, three problems in the
2026-10-08 release need workarounds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. LightOnOCR-3-1B outputs garbage&lt;/strong&gt; (&lt;code&gt;pk pk pk pk …&lt;/code&gt;, &lt;code&gt;是很是很是很…&lt;/code&gt;) and the load
log shows a &lt;code&gt;LOAD REPORT&lt;/code&gt; with every &lt;code&gt;language_model.model.layers.*&lt;/code&gt; key &lt;code&gt;UNEXPECTED&lt;/code&gt; and
every &lt;code&gt;model.language_model.layers.*&lt;/code&gt; key &lt;code&gt;MISSING&lt;/code&gt;. The checkpoint stores the language
model weights under the wrong prefix, so they are dropped and the model runs with
random weights. Older transformers versions behave the same. Rename the keys once:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;python&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;safetensors.torch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;save_file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;LightOnOCR-3-1B/model.safetensors&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;model.language_model.&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;language_model.model.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):]&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startswith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;language_model.model.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;save_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LightOnOCR-3-1B-fixed/model.safetensors&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;format&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# copy the other files (config, tokenizer, processor) next to it; 310 keys are renamed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Load it with &lt;code&gt;LightOnOcrForConditionalGeneration&lt;/code&gt; / &lt;code&gt;LightOnOcrProcessor&lt;/code&gt;. Its
config says &lt;code&gt;model_type: mistral3&lt;/code&gt;, so &lt;code&gt;AutoModelForImageTextToText&lt;/code&gt; picks the wrong class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. LightOnOCR-3-0.8B is about 10× slower than the 4B.&lt;/strong&gt; Its &lt;code&gt;config.json&lt;/code&gt; and
&lt;code&gt;generation_config.json&lt;/code&gt; ship with &lt;code&gt;&amp;quot;use_cache&amp;quot;: false&lt;/code&gt;, so every generated token
recomputes the whole sequence. One page went from 208 s to 14.7 s with identical output:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;python&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_new_tokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6144&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_sample&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;use_cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. The 4B and 0.8B (Qwen3.5 architecture) fall back to slow PyTorch kernels:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;`causal_conv1d_fn` is falling back to its reference PyTorch implementation because `causal_conv1d` is not installed.
`chunk_gated_delta_rule` is falling back to its reference PyTorch implementation because `flash-linear-attention` is not installed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install both. &lt;code&gt;flash-linear-attention&lt;/code&gt; is pure Triton. For &lt;code&gt;causal-conv1d&lt;/code&gt;, use the
prebuilt wheel matching your torch, so that it doesn&#x27;t try to compile against a different
system CUDA. Both worked on an RTX 5080 (sm_120):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;pip&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;flash-linear-attention
pip&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://github.com/Dao-AILab/causal-conv1d/releases/download/v1.7.0/causal_conv1d-1.7.0+cu12torch2.10cxx11abiTRUE-cp312-cp312-linux_x86_64.whl&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For plain transcription, call LightOnOCR-3 with the image only (empty prompt) and
&lt;code&gt;enable_thinking=False&lt;/code&gt; in &lt;code&gt;apply_chat_template&lt;/code&gt; (4B/0.8B). Resize the longest edge
to 2048 px.&lt;/p&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Test set (33 pages, 20,937 ground-truth characters).&lt;/strong&gt; Passages come from
public-domain Aozora Bunko texts (Akutagawa, Dazai, Sōseki, Kenji, Edogawa Ranpo,
Higuchi Ichiyō, Mori Ōgai, Okamoto Kidō, Terada Torahiko and others). Three pages were
written by hand for numbers, acronyms and URLs. Pages were rendered &lt;code&gt;writing-mode: vertical-rl&lt;/code&gt; by headless Chromium in 11 fonts: Noto Serif/Sans CJK, IPA Mincho, and
Google Fonts&#x27; Shippori Mincho, Zen Old Mincho, BIZ UD Mincho and Gothic, Kaisei Tokumin, Zen Maru Gothic,
Klee One and Yuji Syuku.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For each page the passage is &lt;strong&gt;binary-searched to the longest prefix that fits the
page box with no overflow&lt;/strong&gt;. Overflow is measured in the browser with &lt;code&gt;--dump-dom&lt;/code&gt; and
a script that compares &lt;code&gt;scrollWidth&lt;/code&gt;/&lt;code&gt;scrollHeight&lt;/code&gt;. The cut is then backed off to a
clause boundary, so the ground truth is exactly the visible text.&lt;/li&gt;
&lt;li&gt;Furigana come from Aozora&#x27;s own ruby markup (&lt;code&gt;｜漢字《かんじ》&lt;/code&gt;). Paragraphs with gaiji
(&lt;code&gt;※［＃…］&lt;/code&gt;), layout annotations or ／＼ repeat marks are skipped.&lt;/li&gt;
&lt;li&gt;Multi-tier pages use CSS multi-column inside &lt;code&gt;vertical-rl&lt;/code&gt;, which stacks the columns
top-to-bottom like a newspaper.&lt;/li&gt;
&lt;li&gt;Scans are rendered, then degraded with OpenCV: skew + noise + JPEG, 150 dpi binarized,
phone-photo perspective with uneven light and a gutter shadow, mirrored bleed-through,
and a worn photocopy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Scoring.&lt;/strong&gt; Model markup (HTML, Markdown, LaTeX, grounding tokens) is stripped. Then
NFKC (which folds vertical presentation forms such as ︒ ﹁ ﹂ and full-width digits),
dash variants are unified, and all whitespace is removed. Variant kanji (旧/舊) and
historical kana (ゐ/ゑ) are &lt;strong&gt;not&lt;/strong&gt; folded: writing 旧字 as 新字 counts as an error. The
metrics are strict CER, CER without punctuation, order-free error, and a ruby-tolerant
CER that forgives readings written in &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;sup&amp;gt;&lt;/code&gt;, brackets or inline. Every model
ran in plain full-page OCR mode with greedy decoding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveats.&lt;/strong&gt; These are rendered pages, not physical scans; the scan category only
simulates degradation. Aozora texts are public and probably in every model&#x27;s training
data. The test set is 33 pages, so differences under about 0.5 points within one
category are not meaningful.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;dots.ocr-1.5: https://huggingface.co/rednote-hilab/dots.ocr-1.5&lt;/li&gt;
&lt;li&gt;LightOnOCR-3: https://huggingface.co/lightonai/LightOnOCR-3-4B (also -1B, -0.8B)&lt;/li&gt;
&lt;li&gt;chandra-ocr-2: https://huggingface.co/datalab-to/chandra-ocr-2&lt;/li&gt;
&lt;li&gt;Aozora Bunko: https://www.aozora.gr.jp/&lt;/li&gt;
&lt;li&gt;causal-conv1d prebuilt wheels: https://github.com/Dao-AILab/causal-conv1d/releases&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>SP Flash Tool on Linux (MT6592): S_COM_PORT_OPEN_FAIL (1013) in BROM, then &quot;PMT changed for the ROM&quot;</title>
    <link href="https://www.misc2.com/technotes/sp-flash-tool-linux-mt6592-com-port-open-fail-pmt-changed/"/>
    <id>https://www.misc2.com/technotes/sp-flash-tool-linux-mt6592-com-port-open-fail-pmt-changed/</id>
    <published>2026-10-03T00:00:00Z</published>
    <updated>2026-10-03T00:00:00Z</updated>
    <summary>Writing /system on an unrooted MT6592 handheld from Linux. BROM port-open fails because new cdc_acm rejects TIOCCBRK; Download refuses with &quot;PMT changed&quot;. Use SP Flash Tool v5.1648, an LD_PRELOAD shim, Preloader mode and write-memory at the physical address.</summary>
    <category term="firmware"/><category term="mediatek"/><category term="mt6592"/><category term="sp-flash-tool"/><category term="mtkclient"/><category term="android"/><category term="kernel"/><category term="linux"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Goal: replace one file (&lt;code&gt;/system/media/bootanimation.zip&lt;/code&gt;) on an unrooted MediaTek MT6592
Android 5.1 device by writing a modified &lt;code&gt;system&lt;/code&gt; image over USB from Linux. Every tool
failed in a different way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mtkclient&lt;/strong&gt; reads the eMMC fine, but every write hangs forever right after the command
header:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;code&gt;DaHandler - Writing offset 0x5000000 with length 0x100000
DeviceClass - [LIB]: TX:62
...
DeviceClass - [LIB]: TX:00100000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In BROM mode with &lt;code&gt;--preloader&lt;/code&gt;, it got stuck one step earlier instead:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;code&gt;DALegacy - M_EXT_RAM_SIZE : 0xc000000
DALegacy - Uploading stage 2...
DALegacy - Waiting for response ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;SP Flash Tool&lt;/strong&gt; (Linux, console mode &lt;code&gt;flash_tool -i config.xml&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;v5.2228: its &lt;code&gt;MTK_AllInOne_DA.bin&lt;/code&gt; has no MT6592 entry.&lt;/li&gt;
&lt;li&gt;v5.1916: &lt;code&gt;Failed to Connect DA: S_FT_DA_NO_RESPONSE(4001)&lt;/code&gt; right after &lt;code&gt;100% of DA has been sent&lt;/code&gt;, in both BROM and Preloader mode.&lt;/li&gt;
&lt;li&gt;BROM mode (&lt;code&gt;0e8d:0003&lt;/code&gt;, vol-up held), even when run as root:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;code&gt;USB port detected: /dev/ttyACM0
Connect BROM failed: S_COM_PORT_OPEN_FAIL(1013)
[COM] Failed to open COM port.
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Preloader mode (&lt;code&gt;0e8d:2000&lt;/code&gt;) with v5.1648, &amp;quot;Download Only&amp;quot; of just the system partition:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;code&gt;DA Connected
Uncaught BaseException in main(): PMT changed for the ROM; it must be downloaded.
Please select &amp;quot;Format All + Download&amp;quot; scene and try again
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; follow that hint. &amp;quot;Format All + Download&amp;quot; erases the whole device, userdata and
NVRAM included.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Four independent problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;S_COM_PORT_OPEN_FAIL&lt;/code&gt; is not a permissions problem.&lt;/strong&gt; &lt;code&gt;strace&lt;/code&gt; shows the &lt;code&gt;open()&lt;/code&gt; of
&lt;code&gt;/dev/ttyACM0&lt;/code&gt; succeeds. SP Flash Tool then calls &lt;code&gt;ioctl(fd, TIOCCBRK)&lt;/code&gt;, which returns
&lt;code&gt;-1 EOPNOTSUPP&lt;/code&gt;, and the tool gives up. Recent kernels&#x27; &lt;code&gt;cdc_acm&lt;/code&gt; refuses break requests
when the device does not advertise break support in its CDC descriptors. The MediaTek
BROM does not advertise it, while the Preloader does. That is why only BROM mode fails.
Older kernels silently accepted the request, and this 2016–2019 tool assumes that.&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;code&gt;openat(AT_FDCWD, &amp;quot;/dev/ttyACM0&amp;quot;, O_RDWR|O_NOCTTY|O_NONBLOCK) = 10
ioctl(10, TCSETS2, {c_cflag=B115200|CS8|CREAD, ...}) = 0
ioctl(10, TIOCCBRK) = -1 EOPNOTSUPP (Operation not supported)
close(10)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The DA version matters.&lt;/strong&gt; The download agent (DA) in SP Flash Tool v5.1916
(&lt;code&gt;MTK_AllInOne_DA_v3.3001.2019-04-24&lt;/code&gt;) never answers on this board. The one in &lt;strong&gt;v5.1648&lt;/strong&gt;
(&lt;code&gt;v3.3001.00.00&lt;/code&gt;) works. It is byte-identical to the &lt;code&gt;MTK_AllInOne_DA_mt6590.bin&lt;/code&gt; that
mtkclient ships, which is why mtkclient could always connect and read.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;mtkclient can read but not write here.&lt;/strong&gt; With the legacy DA it sends
&lt;code&gt;SDMMC_WRITE_DATA&lt;/code&gt; (0x62) and waits for an ACK that never comes, and it has no timeout.
Separately, in BROM mode it sends the &lt;strong&gt;first&lt;/strong&gt; DRAM (EMI) entry from the preloader rather
than the one matching the eMMC ID. This preloader holds four entries; the matching one was
number 3 (LPDDR3, 1 GB). The first one is LPDDR2, so stage 2 crashes (note the
&lt;code&gt;M_EXT_RAM_SIZE : 0xc000000&lt;/code&gt;). Reordering the entries in a host-side copy of the preloader
fixes the DRAM setup, but the write still hangs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;PMT changed&amp;quot; is SP Flash Tool&#x27;s own arithmetic.&lt;/strong&gt; Its detailed log
(&lt;code&gt;/tmp/SP_FT_Logs/.../QT_FLASH_TOOL.log&lt;/code&gt;) names the partition:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;code&gt;CheckPMTLayoutChange(): PMT changed for &amp;lt;USRDATA&amp;gt;: addr&amp;lt;0x7f800000&amp;gt;--&amp;gt;addr&amp;lt;0x7f800000&amp;gt;,
len&amp;lt;0x151300000&amp;gt;--&amp;gt;len&amp;lt;0x151b80000&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tool auto-sizes the last partition (userdata) to fill the eMMC, but counts
boot1 + boot2 + RPMB (4 + 4 + 0.5 MB = 0x880000) into the total. The device&#x27;s real table,
written at the factory, does not. The scatter file can&#x27;t override this, because the tool
recomputes the size.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the raw &lt;strong&gt;&lt;code&gt;write-memory&lt;/code&gt;&lt;/strong&gt; command instead of &amp;quot;Download&amp;quot;. It writes a file to an explicit
physical eMMC address and skips the partition-table comparison. It is also unforgiving: you
own the address, so take a full dump first and verify placement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Dump everything and find the real layout.&lt;/strong&gt; Use mtkclient (reads work). MT65xx devices
keep a &amp;quot;PMT&amp;quot; table near the end of the user area, with magic &lt;code&gt;1vTP1.1&lt;/code&gt;, 96-byte entries
(&lt;code&gt;name[64]&lt;/code&gt;, &lt;code&gt;size u64&lt;/code&gt;, &lt;code&gt;region u64&lt;/code&gt; (1 = boot1, 8 = user), &lt;code&gt;offset u64&lt;/code&gt;, &lt;code&gt;mask u64&lt;/code&gt;), and
a mirror 4 KB later. Here &lt;code&gt;ANDROID&lt;/code&gt; (system) was at physical &lt;code&gt;0x5000000&lt;/code&gt;, size &lt;code&gt;0x60000000&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Shim the break ioctl&lt;/strong&gt; so BROM mode works too (Preloader mode does not need it, but it
does no harm):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;c&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// brkshim.c — gcc -shared -fPIC -O2 -o brkshim.so brkshim.c -ldl&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define _GNU_SOURCE&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cpf&quot;&gt;&amp;lt;dlfcn.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cpf&quot;&gt;&amp;lt;errno.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cpf&quot;&gt;&amp;lt;stdarg.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cpf&quot;&gt;&amp;lt;sys/ioctl.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cpf&quot;&gt;&amp;lt;termios.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...);&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dlsym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RTLD_NEXT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ioctl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;va_list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;va_start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;va_arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;va_end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errno&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EOPNOTSUPP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TIOCSBRK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TIOCCBRK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errno&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. Run SP Flash Tool v5.1648&lt;/strong&gt; in console mode with the shim. It also needs
&lt;code&gt;libpng12.so.0&lt;/code&gt;, which ships in the &lt;code&gt;lib/&lt;/code&gt; of later SP Flash Tool zips:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;sh&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/SP_Flash_Tool_v5.1648_Linux
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$T&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
sudo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;env&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LD_PRELOAD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/brkshim.so&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$T&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$T&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/lib&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;QT_QPA_PLATFORM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;offscreen&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;./flash_tool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-r&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-i&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/path/to/writemem.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then plug the device in powered off, &lt;strong&gt;with no buttons held&lt;/strong&gt; (Preloader mode, &lt;code&gt;0e8d:2000&lt;/code&gt;).
In this mode the preloader has already initialised DRAM, so no EMI settings are needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. The &lt;code&gt;write-memory&lt;/code&gt; config.&lt;/strong&gt; A scatter file is still required. Generate it from the PMT
(all partitions, &lt;code&gt;linear_start_addr&lt;/code&gt; = &lt;code&gt;physical_start_addr&lt;/code&gt;) and add &lt;code&gt;skip_pmt_operate: true&lt;/code&gt;
to its &lt;code&gt;general&lt;/code&gt; → &lt;code&gt;info&lt;/code&gt; block so the tool never rewrites the table. If the scatter marks
nothing &lt;code&gt;is_download: true&lt;/code&gt;, the tool aborts during &amp;quot;auto load all roms&amp;quot; (&lt;code&gt;DL_AutoLoad failed&lt;/code&gt;). So mark exactly one partition downloadable and give it a harmless, disabled rom
(here: the original, unmodified system image):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;xml&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;flashtool-config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;general&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;chip-name&amp;gt;&lt;/span&gt;MT6592&lt;span class=&quot;nt&quot;&gt;&amp;lt;/chip-name&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;storage-type&amp;gt;&lt;/span&gt;EMMC&lt;span class=&quot;nt&quot;&gt;&amp;lt;/storage-type&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;download-agent&amp;gt;&lt;/span&gt;/path/to/SP_Flash_Tool_v5.1648_Linux/MTK_AllInOne_DA.bin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/download-agent&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;scatter&amp;gt;&lt;/span&gt;/path/to/MT6592_writemem_scatter.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/scatter&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;rom-list&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;rom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;index=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;17&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enable=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;partition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ANDROID&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;/path/to/system_original.img&lt;span class=&quot;nt&quot;&gt;&amp;lt;/rom&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/rom-list&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;connection&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BromUSB&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;high-speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;power=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AutoDetect&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;timeout-count=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3600000&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;com-port=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/general&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;commands&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;write-memory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;write-memory-item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;input-mode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FromFile&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;program-mode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PageOnly&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addr-mode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PhysicalAddress&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;address=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0x5000000&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;input-length=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0x60000000&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;part-id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;/path/to/system_new.img&lt;span class=&quot;nt&quot;&gt;&amp;lt;/write-memory-item&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/write-memory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/commands&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/flashtool-config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;part-id=&amp;quot;8&amp;quot;&lt;/code&gt; is the eMMC user area. Only one &lt;code&gt;write-memory-item&lt;/code&gt; is allowed per config.&lt;/p&gt;
&lt;p&gt;Result: &lt;code&gt;100% of data write to memory, (1610612736 /1610612736)&lt;/code&gt; in about 2.5 minutes
(about 10 MB/s), and the device booted with the new boot animation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveats&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prove placement first.&lt;/strong&gt; Before the real write, I wrote 1 MiB of random bytes into the
middle of the CACHE partition (disposable), read an 18 MB window back with mtkclient, and
confirmed it landed at exactly the requested address with nothing else changed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always read back.&lt;/strong&gt; The first full 1.5 GB attempt stalled after one 1 MiB packet (no CPU,
no progress for 30+ minutes). It did not happen again on retries (2 MiB, 94 MiB, then
1.5 GB all completed), so I have no explanation for it. A read-back showed exactly one new
1 MiB chunk had been written.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The image must be valid for Android&lt;/strong&gt;: &lt;code&gt;e2fsck -fn&lt;/code&gt; clean, and the replaced files must
keep their owner, mode and &lt;code&gt;security.selinux&lt;/code&gt; xattr (&lt;code&gt;u:object_r:system_file:s0&lt;/code&gt;).
Check with &lt;code&gt;debugfs -R &#x27;ea_list /media/bootanimation.zip&#x27; system.img&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A hung DA leaves the device unresponsive.&lt;/strong&gt; It keeps running on battery and ignores
buttons, and the host logs &lt;code&gt;device descriptor read/64, error -71&lt;/code&gt;. Hold power for
15–20 seconds to reset it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BROM mode needs vol-up held at the moment the chip powers up.&lt;/strong&gt; Unplug everything, hold
vol-up, then connect the data cable. With external power attached it tends to land in
Preloader mode instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;strace -f -e trace=openat,ioctl,read,write&lt;/code&gt; on &lt;code&gt;flash_tool&lt;/code&gt; showed the &lt;code&gt;TIOCCBRK&lt;/code&gt; failure
and, later, the per-packet protocol: &lt;code&gt;Z&lt;/code&gt;, 1 MiB data, 2-byte checksum, then the device
replies &lt;code&gt;i&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The kernel log showed BROM enumerating cleanly (&lt;code&gt;cdc_acm …: ttyACM0: USB ACM device&lt;/code&gt;), so
the cable and port were fine.&lt;/li&gt;
&lt;li&gt;Comparing sha256 sums of the &lt;code&gt;MTK_AllInOne_DA.bin&lt;/code&gt; files showed mtkclient&#x27;s working DA is
the one from v5.1648. Parsing the DA header (&lt;code&gt;0xDADA&lt;/code&gt; entries with hw code) showed which
tool versions still list MT6592.&lt;/li&gt;
&lt;li&gt;SP Flash Tool&#x27;s detailed logs in &lt;code&gt;/tmp/SP_FT_Logs/&lt;/code&gt; give the exact reason behind generic
errors such as &amp;quot;PMT changed&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;mtkclient: &lt;a href=&quot;https://github.com/bkerler/mtkclient&quot;&gt;https://github.com/bkerler/mtkclient&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SP Flash Tool Linux builds (older versions are still on the CDN):
&lt;code&gt;https://cdn.spflashtools.com/wp-content/uploads/SP_Flash_Tool_v5.1648_Linux.zip&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Chromium window drag warps the window (xfwm4) or the cursor (KWin) on X11</title>
    <link href="https://www.misc2.com/technotes/chromium-x11-drag-warp-net-wm-moveresize/"/>
    <id>https://www.misc2.com/technotes/chromium-x11-drag-warp-net-wm-moveresize/</id>
    <published>2026-10-03T00:00:00Z</published>
    <updated>2026-10-03T00:00:00Z</updated>
    <summary>Dragging a Chromium window by its own title bar on X11 makes the window jump hundreds of pixels (often off-screen) under xfwm4, or makes the mouse cursor jump under KWin. Chromium sends a _NET_WM_MOVERESIZE request with wrong root coordinates; turning on &quot;Use system title bar and borders&quot; avoids it.</summary>
    <category term="x11"/><category term="chromium"/><category term="xfwm4"/><category term="kwin"/><category term="window-manager"/><category term="csd"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On an X11 desktop, Chromium windows &amp;quot;get lost&amp;quot;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You start dragging a Chromium window by its tab strip or title area and the window
&lt;strong&gt;jumps&lt;/strong&gt; by several hundred pixels, often mostly &lt;strong&gt;off-screen&lt;/strong&gt; (only a thin strip left
visible, sometimes behind the panel).&lt;/li&gt;
&lt;li&gt;Clicks land in the wrong place for a while, as if the window were somewhere other than
where it is drawn.&lt;/li&gt;
&lt;li&gt;It looks random: a given window does it &lt;strong&gt;once&lt;/strong&gt;, then behaves until some later point.&lt;/li&gt;
&lt;li&gt;After switching the window manager from xfwm4 to KWin, the window stops jumping, but now
the &lt;strong&gt;mouse cursor&lt;/strong&gt; jumps 10–350 px at the start of a drag.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other applications that use the window manager&#x27;s own title bar are not affected.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chromium draws its own title bar (client-side decorations, the default). When you drag that
title bar, Chromium does not move the window itself. It asks the window manager to start an
interactive move by sending an EWMH
&lt;a href=&quot;https://specifications.freedesktop.org/wm/latest/ar01s04.html#id-1.5.4&quot;&gt;&lt;code&gt;_NET_WM_MOVERESIZE&lt;/code&gt;&lt;/a&gt;
client message. That message contains &lt;code&gt;x_root, y_root&lt;/code&gt;: the pointer position in root
(screen) coordinates, as the client believes it to be.&lt;/p&gt;
&lt;p&gt;Chromium sometimes sends &lt;strong&gt;wrong&lt;/strong&gt; root coordinates. Our capture under KWin:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;SendEvent  sender=Chromium  type=_NET_WM_MOVERESIZE  data=(1413, 203, 8, 0, 0)
WarpPointer  sender=KWin  dst=(1413, 203)
pointer motion  (1606,52) -&amp;gt; (1413,203)   344 px in 1 ms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Direction &lt;code&gt;8&lt;/code&gt; is &lt;code&gt;_NET_WM_MOVERESIZE_MOVE&lt;/code&gt;. The real pointer was at &lt;strong&gt;(1606, 52)&lt;/strong&gt;. Chromium
claimed &lt;strong&gt;(1413, 203)&lt;/strong&gt;, an error of (193, 151) px. The most likely explanation is that
Chromium works out the root position from its &lt;strong&gt;cached window bounds&lt;/strong&gt; plus the pointer&#x27;s
offset inside the window, and the cache is stale (for example after the window manager has
moved or placed the window). That fits the &amp;quot;each window does it once&amp;quot; pattern: after the
jump, the cache matches reality again.&lt;/p&gt;
&lt;p&gt;The two window managers handle the bad coordinates differently:&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Window manager&lt;/th&gt;
&lt;th&gt;What it does with &lt;code&gt;x_root, y_root&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;What you see&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;xfwm4&lt;/strong&gt; 4.20&lt;/td&gt;
&lt;td&gt;Uses them as the grab origin for the move: window x = start x + (pointer x − press x), with a wrong start&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;window&lt;/strong&gt; jumps by the error (we measured −316, −332 and −649 px)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KWin&lt;/strong&gt; 6.7 (X11)&lt;/td&gt;
&lt;td&gt;Warps the pointer to the given position before starting the move (&lt;code&gt;X11Window::NETMoveResize&lt;/code&gt;: &lt;em&gt;&amp;quot;move cursor to the provided position to prevent the window jumping there on first movement&amp;quot;&lt;/em&gt;)&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;cursor&lt;/strong&gt; jumps by the error; the window stays put&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;So the bug is in the client&#x27;s request, and each window manager shows it in its own way.
KWin&#x27;s behaviour is documented in its source. The xfwm4 part was measured from window
geometry before and after each jump. Our early request tracer under xfwm4 did not log
&lt;code&gt;SendEvent&lt;/code&gt;, so the bad payload was only captured directly under KWin.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Turn off Chromium&#x27;s client-side title bar so that moves are started by the window manager
from the real pointer event:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chromium → Settings → Appearance → &amp;quot;Use system title bar and borders&amp;quot; → On&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a per-profile setting, so turn it on in every profile you use. After the change,
the window manager draws the title bar (&lt;code&gt;xprop _NET_FRAME_EXTENTS&lt;/code&gt; on the window shows a
non-zero top extent), no &lt;code&gt;_NET_WM_MOVERESIZE&lt;/code&gt; is sent for ordinary drags, and the jumps stop.
This was confirmed working on the affected machine.&lt;/p&gt;
&lt;p&gt;The same reasoning applies to other client-side-decorated (GTK/libadwaita) apps that show the
same symptom: anything that makes the window manager draw the title bar avoids the
client-supplied coordinates.&lt;/p&gt;
&lt;p&gt;Changing the window manager alone is not a fix. It only changes which thing jumps.&lt;/p&gt;
&lt;h3 id=&quot;recovering-a-window-that-is-already-off-screen&quot;&gt;Recovering a window that is already off-screen &lt;a class=&quot;header-anchor&quot; href=&quot;#recovering-a-window-that-is-already-off-screen&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;xdotool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;search&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--name&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;Chromium&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;w&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;xdotool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;getwindowname&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$w&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;xdotool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;getwindowgeometry&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$w&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
xdotool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;windowmove&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;window-id&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The jumps were rare, so it took permanent watchers, run as systemd user units on both X
displays, until one was caught with numbers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Window geometry&lt;/strong&gt; watcher: logs every move of top-level windows and flags any window
that ends up mostly off-screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pointer&lt;/strong&gt; watcher: logs button presses and motion during drags (no keys).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request&lt;/strong&gt; watcher: uses the XRecord extension to log every &lt;code&gt;SendEvent&lt;/code&gt; ClientMessage any
client sends, with the sender&#x27;s resource-ID base, so you can see &lt;em&gt;who&lt;/em&gt; asked for a move
and with what coordinates.&lt;/li&gt;
&lt;li&gt;A short-lived probe logging every &lt;code&gt;XWarpPointer&lt;/code&gt; request and its sender, which is what
showed KWin moving the cursor ~150 ms after button press (Chromium&#x27;s drag threshold).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The request watcher is the useful one. It is short and needs only &lt;code&gt;python-xlib&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;python&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;&quot;&quot;&quot;Log SendEvent ClientMessages (e.g. _NET_WM_MOVERESIZE) via XRecord.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;Usage: xreqlog.py DISPLAY OUTFILE&quot;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Xlib&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;display&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Xlib.ext&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;SEND_EVENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CLIENT_MESSAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;33&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buffering&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;aname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_atom_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromClient&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client_swapped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;little&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEND_EVENT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;44&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CLIENT_MESSAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;win&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;little&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;little&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;vals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unpack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;5i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isoformat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timespec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;milliseconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; id_base=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id_base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; win=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;win&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt;
                    &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; data=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vals&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record_create_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AllClients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;core_requests&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;core_replies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;ext_requests&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ext_replies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;delivered_events&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;device_events&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;errors&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;client_started&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_died&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record_enable_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record_free_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compare the &lt;code&gt;data=(x, y, 8, …)&lt;/code&gt; of a &lt;code&gt;_NET_WM_MOVERESIZE&lt;/code&gt; line with where the pointer
actually was at that moment (&lt;code&gt;xdotool getmouselocation&lt;/code&gt;, or a pointer logger). If they
differ, you have this bug.&lt;/p&gt;
&lt;p&gt;Things that were checked and ruled out along the way: X Shape input regions (none on the
Chromium windows), compositor drawing offsets (screenshots matched the X window tree), and
Chromium&#x27;s saved window state (it never contained the off-screen positions).&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EWMH spec, &lt;a href=&quot;https://specifications.freedesktop.org/wm/latest/ar01s04.html#id-1.5.4&quot;&gt;&lt;code&gt;_NET_WM_MOVERESIZE&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;KDE bug &lt;a href=&quot;https://bugs.kde.org/show_bug.cgi?id=449105&quot;&gt;449105&lt;/a&gt;, &amp;quot;Dragging a window that was
opened maximized moves the mouse cursor to the top left corner of the window&amp;quot; (Wayland;
fixed in Plasma 6.1). A related cursor-jump issue, different trigger.&lt;/li&gt;
&lt;li&gt;KWin merge requests &lt;a href=&quot;https://invent.kde.org/plasma/kwin/-/merge_requests/5399&quot;&gt;5399&lt;/a&gt;
(&amp;quot;Make Window::interactiveMoveOffset() proportional&amp;quot;) and
&lt;a href=&quot;https://invent.kde.org/plasma/kwin/-/merge_requests/5421&quot;&gt;5421&lt;/a&gt; (&amp;quot;x11: Fix interactive
move offset&amp;quot;).&lt;/li&gt;
&lt;li&gt;Unrelated noise you may see at the same time with xfwm4 4.20 + picom: a flood of
&lt;code&gt;GLib-CRITICAL: g_hash_table_lookup&lt;/code&gt; in &lt;code&gt;~/.xsession-errors&lt;/code&gt; (xfwm4 issue #855, cosmetic).&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>No audio in Chrome Remote Desktop session on PipeWire: &quot;ALSA: Couldn&#x27;t open audio device: Host is down&quot;</title>
    <link href="https://www.misc2.com/technotes/chrome-remote-desktop-pipewire-no-audio-host-is-down/"/>
    <id>https://www.misc2.com/technotes/chrome-remote-desktop-pipewire-no-audio-host-is-down/</id>
    <published>2026-09-11T00:00:00Z</published>
    <updated>2026-09-11T00:00:00Z</updated>
    <summary>In a Chrome Remote Desktop X session on PipeWire, apps were silent or failed with &quot;Host is down&quot;. CRD puts PULSE_RUNTIME_PATH, PULSE_SINK and PIPEWIRE_REMOTE into the session, pointing at its own PipeWire, which had not started. Unset them before dbus-launch, bridge system PipeWire to CRD&#x27;s FIFO, check sink volume.</summary>
    <category term="chrome-remote-desktop"/><category term="pipewire"/><category term="audio"/><category term="dbus"/><category term="arch"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Connected to a Linux desktop with Chrome Remote Desktop (CRD), no sound reached the CRD
client. In a terminal inside the CRD session:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;$ mpg123 beep.mp3
Couldn&#x27;t open SDL audio: ALSA: Couldn&#x27;t open audio device: Host is down
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A GUI media player (Parole) &amp;quot;played&amp;quot; an MP3 in silence, and no sink-input for it ever
appeared in &lt;code&gt;pactl list sink-inputs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Command-line PipeWire tools (&lt;code&gt;pw-cli&lt;/code&gt;, &lt;code&gt;pw-dump&lt;/code&gt;) run in the session quietly talked to the
wrong PipeWire instance.&lt;/li&gt;
&lt;li&gt;After a first attempt at a fix, some apps started over D-Bus failed in libpulse with:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;Failed to create secure directory ()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Three separate problems were stacked. Each one alone is enough to leave the CRD client
silent.&lt;/p&gt;
&lt;h3 id=&quot;1-crds-audio-variables-leak-into-the-whole-session&quot;&gt;1. CRD&#x27;s audio variables leak into the whole session &lt;a class=&quot;header-anchor&quot; href=&quot;#1-crds-audio-variables-leak-into-the-whole-session&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CRD starts the remote session with three variables that point at a PipeWire instance of its
own, which is supposed to write audio into a FIFO that the CRD host reads:&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value set by CRD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PULSE_RUNTIME_PATH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/tmp/pyxdg-runtime-dir-fallback-&amp;lt;user&amp;gt;/crd_audio#&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PULSE_SINK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chrome_remote_desktop_session&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PIPEWIRE_REMOTE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;crd_audio#&amp;lt;id&amp;gt;/pipewire&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;On this machine CRD&#x27;s own PipeWire never came up: it failed, CRD retried it five times, then
started the host without it. (Our reading of why: the CRD session had no &lt;code&gt;XDG_RUNTIME_DIR&lt;/code&gt;
and no systemd user bus, so that PipeWire found no runtime directory.) The variables were
still set, and everything started from the session script inherits them: the desktop
session, panel, file manager, terminals, shells, and the &lt;code&gt;dbus-launch&lt;/code&gt; session bus.&lt;/p&gt;
&lt;p&gt;ALSA clients going through the PipeWire ALSA plugin (mpg123&#x27;s default output, GStreamer apps)
follow &lt;code&gt;PIPEWIRE_REMOTE&lt;/code&gt; to the dead instance and fail with &lt;code&gt;Host is down&lt;/code&gt;. Setting
&lt;code&gt;PULSE_SERVER&lt;/code&gt; in &lt;code&gt;.bashrc&lt;/code&gt; does not help them: it only affects libpulse clients, and only in
interactive shells.&lt;/p&gt;
&lt;h3 id=&quot;2-empty-values-pushed-into-the-d-bus-activation-environment&quot;&gt;2. Empty values pushed into the D-Bus activation environment &lt;a class=&quot;header-anchor&quot; href=&quot;#2-empty-values-pushed-into-the-d-bus-activation-environment&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;dbus-launch&lt;/code&gt; copies its own environment into the activation environment of the session bus,
so D-Bus-activated services (for example &lt;code&gt;xdg-desktop-portal*&lt;/code&gt;) got the bad variables too.
&lt;code&gt;dbus-update-activation-environment&lt;/code&gt; can set variables but cannot unset them, and the first
attempt &amp;quot;cleared&amp;quot; them by setting them to empty strings. An empty &lt;code&gt;PULSE_RUNTIME_PATH&lt;/code&gt; makes
libpulse fail with &lt;code&gt;Failed to create secure directory ()&lt;/code&gt;. We confirmed this by reproducing it.&lt;/p&gt;
&lt;h3 id=&quot;3-the-crd-sink-came-back-at-19--volume&quot;&gt;3. The CRD sink came back at 19 % volume &lt;a class=&quot;header-anchor&quot; href=&quot;#3-the-crd-sink-came-back-at-19--volume&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The working setup routes the system PipeWire into CRD&#x27;s FIFO through a pipe-sink (see Fix).
WirePlumber restores the last stored volume of a re-created sink; for this sink that was
19 % (-43 dB). So even when routing was correct, the audio was nearly silent.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;strip-the-variables-before-dbus-launch&quot;&gt;Strip the variables before &lt;code&gt;dbus-launch&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;#strip-the-variables-before-dbus-launch&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;~/.chrome-remote-desktop-session&lt;/code&gt;, put this at the very top, before &lt;code&gt;dbus-launch&lt;/code&gt; and
before the desktop session starts:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;unset&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_RUNTIME_PATH&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_SINK&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PIPEWIRE_REMOTE
&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/run/user/&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;id&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-u&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;dbus-launch&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;startlxqt&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# or your session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the session bus is started after the &lt;code&gt;unset&lt;/code&gt;, its activation environment is clean
too. Apps now use the normal per-user PipeWire.&lt;/p&gt;
&lt;p&gt;For shells that still inherit the variables (for example in a session started before this
change), unset them in your shell startup file as well:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;unset&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_RUNTIME_PATH&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_SINK&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PIPEWIRE_REMOTE
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;bridge-the-system-pipewire-into-crds-fifo&quot;&gt;Bridge the system PipeWire into CRD&#x27;s FIFO &lt;a class=&quot;header-anchor&quot; href=&quot;#bridge-the-system-pipewire-into-crds-fifo&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With CRD&#x27;s own PipeWire not running, nothing writes to the FIFO the host reads. Load a pipe
sink into the system PipeWire (through pipewire-pulse) pointing at that FIFO, and make it the
default:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;CRD_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;find&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/tmp/pyxdg-runtime-dir-fallback-&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/run/user/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$UID&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;-maxdepth&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-type&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;d&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-name&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;crd_audio*&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&amp;gt;/dev/null&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;head&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-1&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;CRD_FIFO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CRD_DIR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/fifo_output&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-p&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CRD_FIFO&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;mkfifo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CRD_FIFO&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

pactl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;load-module&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;module-pipe-sink&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sink_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;crd_sink&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CRD_FIFO&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;48000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;s16le
pactl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;set-default-sink&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;crd_sink
pactl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;set-sink-volume&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;crd_sink&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;%&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# WirePlumber may restore an old low level&lt;/span&gt;
pactl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;set-sink-mute&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;crd_sink&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This has to run after the CRD host is up, because the FIFO directory belongs to the host&#x27;s
session. Running it late is fine: the host picks up data from the FIFO whenever it arrives.
We start it from the session script after a fixed &lt;code&gt;sleep 20&lt;/code&gt;. That delay is a guess at the
host&#x27;s start-up time, not a measured value.&lt;/p&gt;
&lt;h3 id=&quot;if-something-must-update-the-d-bus-activation-environment-set-real-values&quot;&gt;If something must update the D-Bus activation environment, set real values &lt;a class=&quot;header-anchor&quot; href=&quot;#if-something-must-update-the-d-bus-activation-environment-set-real-values&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you have a switcher script (or anything else) that fixes the activation environment of an
already-running session bus, set working defaults instead of empty strings:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;dbus-update-activation-environment&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PIPEWIRE_REMOTE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;pipewire-0&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PULSE_RUNTIME_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/run/user/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$UID&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/pulse&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(The script on the affected machine also passes &lt;code&gt;PULSE_SINK=&lt;/code&gt;. Only an empty
&lt;code&gt;PULSE_RUNTIME_PATH&lt;/code&gt; was shown to break libpulse. The repaired session ran with an empty
&lt;code&gt;PULSE_SINK&lt;/code&gt; and played audio, but that was not tested separately.)&lt;/p&gt;
&lt;h3 id=&quot;repairing-a-session-that-is-already-running&quot;&gt;Repairing a session that is already running &lt;a class=&quot;header-anchor&quot; href=&quot;#repairing-a-session-that-is-already-running&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Processes that are already running keep the variables they started with. Without logging
out, restart each &lt;em&gt;launcher&lt;/em&gt; (panel, desktop/file manager, app runner, hotkey daemon, window
manager, notification daemon, &lt;code&gt;xdg-desktop-portal*&lt;/code&gt;) with a clean environment so its children
inherit it. The CRD session bus is the &lt;code&gt;dbus-launch&lt;/code&gt; one, not &lt;code&gt;/run/user/&amp;lt;uid&amp;gt;/bus&lt;/code&gt;, so take
its address from a session process:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;DBUS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;tr&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;\0&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;\n&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/proc/&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;pgrep&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-x&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;lxqt-session&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;/environ&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;grep&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;^DBUS_SESSION_BUS_ADDRESS=&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;cut&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-d&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-f2-&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
env&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-u&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_RUNTIME_PATH&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-u&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_SINK&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-u&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PIPEWIRE_REMOTE&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-u&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;PULSE_SERVER&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DISPLAY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DISPLAY&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/run/user/&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;id&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-u&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DBUS_SESSION_BUS_ADDRESS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DBUS&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;setsid&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-f&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;lxqt-panel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;D-Bus-activated services such as &lt;code&gt;xdg-desktop-portal*&lt;/code&gt; can simply be killed after the
activation environment is fixed; they restart on demand with the new environment. Kill them
by PID (&lt;code&gt;pgrep -f &#x27;^/usr/lib/xdg-desktop-portal&#x27;&lt;/code&gt;): &lt;code&gt;pkill -f xdg-desktop-portal-&lt;/code&gt; also
matched the command line of the shell running it and killed that shell.&lt;/p&gt;
&lt;h3 id=&quot;what-was-verified&quot;&gt;What was verified &lt;a class=&quot;header-anchor&quot; href=&quot;#what-was-verified&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A beep played with &lt;code&gt;mpg123&lt;/code&gt; in a CRD terminal was heard in the CRD client.&lt;/li&gt;
&lt;li&gt;The media player, relaunched with a clean environment, had a sink-input on &lt;code&gt;crd_sink&lt;/code&gt;,
uncorked, at 100 %.&lt;/li&gt;
&lt;li&gt;Freshly D-Bus-activated &lt;code&gt;xdg-desktop-portal*&lt;/code&gt; processes got &lt;code&gt;PIPEWIRE_REMOTE=pipewire-0&lt;/code&gt;
and a valid &lt;code&gt;PULSE_RUNTIME_PATH&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not yet verified: a complete new CRD session using the updated session script. The session
in this write-up was repaired in place.&lt;/p&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Look for leaked variables in any process:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;tr&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;\0&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;\n&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/proc/&amp;lt;pid&amp;gt;/environ&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;grep&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-E&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;PULSE|PIPEWIRE&#x27;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PipeWire tools inside a CRD shell need the variable removed, or they talk to the dead
instance: &lt;code&gt;env -u PIPEWIRE_REMOTE pw-cli ...&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;busctl --user&lt;/code&gt; and &lt;code&gt;systemctl --user show-environment&lt;/code&gt; look at the systemd user bus, which
is the wrong bus for a CRD session started with &lt;code&gt;dbus-launch&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check that the CRD host is reading the FIFO. &lt;code&gt;lsof&lt;/code&gt; should show the host with it open for
reading and &lt;code&gt;pipewire-pulse&lt;/code&gt; with it open read/write:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;lsof&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/tmp/pyxdg-runtime-dir-fallback-&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;/crd_audio*/fifo_output
timeout&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;dd&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/zero&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/tmp/pyxdg-runtime-dir-fallback-&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;/crd_audio*/fifo_output&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;dd&lt;/code&gt; (400 KiB) finished in about 1.8 s, which matches 48 kHz 16-bit stereo
(192 kB/s). If it hangs, the host is not reading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pactl get-sink-volume crd_sink&lt;/code&gt; showed the 19 % level.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>systemd unit restarted 2 million times unnoticed: &quot;Failed to load environment files: No such file or directory&quot;</title>
    <link href="https://www.misc2.com/technotes/systemd-unit-restart-loop-failed-to-load-environment-files/"/>
    <id>https://www.misc2.com/technotes/systemd-unit-restart-loop-failed-to-load-environment-files/</id>
    <published>2026-09-10T00:00:00Z</published>
    <updated>2026-09-10T00:00:00Z</updated>
    <summary>Template units whose EnvironmentFile and user had been deleted restart-looped every 3 s for 73 days (1,958,017 restarts each). systemd fails before spawning a process, so CPU and ps show nothing, and RestartSec=3 stays under the start limit. Find them by NRestarts; vacuum the journal by size.</summary>
    <category term="systemd"/><category term="journald"/><category term="linux"/><category term="monitoring"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nothing visible: no alerts, no CPU load, no stray processes. A sweep for restart loops found
three instances of a template unit, left enabled after the application they belonged to had been
removed (its user, home directory and files were gone):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;ini&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;[Unit]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Old agent bridge (%i)&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;network-online.target&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[Service]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;olduser&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;WorkingDirectory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/home/olduser/runtime&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;EnvironmentFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/home/olduser/agents/%i/agent.env&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/bin/python3 /home/olduser/runtime/bridge.py&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;always&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;RestartSec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The journal had this every 3 seconds per instance, for 73 days:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;myapp@a.service: Scheduled restart job, restart counter is at 1958017.
myapp@a.service: Failed to load environment files: No such file or directory
myapp@a.service: Failed to spawn &#x27;start&#x27; task: No such file or directory
myapp@a.service: Failed with result &#x27;resources&#x27;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of the three instances had restarted 1,958,017 times (about 5.9 million in total). A
related daemon those units had depended on had been in &lt;code&gt;failed&lt;/code&gt; state for the same 73 days.&lt;/p&gt;
&lt;p&gt;The journal had grown to 4 G, almost all of it from this loop.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Two things combined to keep it invisible:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No process is ever started.&lt;/strong&gt; &lt;code&gt;EnvironmentFile=&lt;/code&gt; points to a file that no longer exists, so
systemd fails the start &lt;em&gt;before&lt;/em&gt; &lt;code&gt;ExecStart=&lt;/code&gt; runs (&lt;code&gt;Failed with result &#x27;resources&#x27;&lt;/code&gt;). The
unit status showed &lt;code&gt;Mem peak: 0B, CPU: 0&lt;/code&gt;. CPU or memory monitoring, &lt;code&gt;ps&lt;/code&gt; and the load average
all see nothing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;RestartSec=3&lt;/code&gt; stays under the start rate limit.&lt;/strong&gt; With the defaults (&lt;code&gt;StartLimitBurst=5&lt;/code&gt;
starts within &lt;code&gt;StartLimitIntervalSec=10s&lt;/code&gt;), a fast loop is stopped and left in &lt;code&gt;failed&lt;/code&gt; state.
One start every 3 seconds is about 3 to 4 starts per 10 seconds, which never reaches 5, so
&lt;code&gt;Restart=always&lt;/code&gt; keeps going forever.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The units were orphans: the application was removed, but its unit files stayed enabled.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Stop and disable the units, then clear the failed state:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;disable&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--now&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;myapp@a.service&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;myapp@b.service&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;myapp@c.service
systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;reset-failed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then remove the leftover unit file (and any drop-in directories for related units) and reload:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;rm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-f&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/etc/systemd/system/myapp@.service
systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;daemon-reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards: no unit files left for the old application, and no units in &lt;code&gt;failed&lt;/code&gt; or
&lt;code&gt;activating&lt;/code&gt; state.&lt;/p&gt;
&lt;h3 id=&quot;journal-cleanup-size-not-time&quot;&gt;Journal cleanup: size, not time &lt;a class=&quot;header-anchor&quot; href=&quot;#journal-cleanup-size-not-time&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;journalctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--vacuum-size&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;500M&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# 4G -&amp;gt; 467.8M, freed about 3.5G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;journalctl --vacuum-time=30d&lt;/code&gt; freed &lt;strong&gt;0 B&lt;/strong&gt; here. Time-based vacuuming only removes archived
journal files whose newest entry is older than the cutoff. The loop rotated a new 80 MB file
about every 24 minutes, so every retained file was recent. Vacuuming by size works.&lt;/p&gt;
&lt;p&gt;Check journal size &lt;strong&gt;as root&lt;/strong&gt;. As an ordinary user, &lt;code&gt;journalctl --disk-usage&lt;/code&gt; reported only that
user&#x27;s own journal (&lt;code&gt;8M&lt;/code&gt;), while the system total was 467.8 M after cleanup.&lt;/p&gt;
&lt;h3 id=&quot;removing-the-orphaned-package&quot;&gt;Removing the orphaned package &lt;a class=&quot;header-anchor&quot; href=&quot;#removing-the-orphaned-package&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The related daemon&#x27;s package was removed with &lt;code&gt;pacman -Rns&lt;/code&gt;. Preview the cascade first:
&lt;code&gt;pacman -Rs --print &amp;lt;pkg&amp;gt;&lt;/code&gt; (pacman rejects &lt;code&gt;-Rns --print&lt;/code&gt;, because &lt;code&gt;--nosave&lt;/code&gt; and &lt;code&gt;--print&lt;/code&gt;
cannot be combined). The preview listed a password-hashing library among the dependencies to be
removed; &lt;code&gt;pacman -Qi &amp;lt;lib&amp;gt;&lt;/code&gt; showed &lt;code&gt;Required By:&lt;/code&gt; only the package being removed, so it was safe.&lt;/p&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;List every service with a non-trivial restart counter:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;list-units&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--type&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;service&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--all&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--no-legend&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--plain&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;awk&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;{print $1}&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;u&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;show&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-p&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;NRestarts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--value&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&amp;gt;/dev/null&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;*&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; NRestarts=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also useful:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;list-units&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--state&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;failed,activating&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A unit that keeps showing &lt;code&gt;activating (auto-restart)&lt;/code&gt; is in a restart loop.&lt;/p&gt;
&lt;p&gt;The same sweep on the same day found a different crash loop that used 2.24 CPU cores. That one
was easy to see once CPU per cgroup was measured, but it would not stand out in restart counts.
A sweep needs both: CPU per cgroup &lt;strong&gt;and&lt;/strong&gt; &lt;code&gt;NRestarts&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;systemd.unit(5)&lt;/code&gt;: &lt;code&gt;StartLimitIntervalSec=&lt;/code&gt;, &lt;code&gt;StartLimitBurst=&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;systemd.exec(5)&lt;/code&gt;: &lt;code&gt;EnvironmentFile=&lt;/code&gt; (a missing file fails the start unless the path is
prefixed with &lt;code&gt;-&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;journalctl(1)&lt;/code&gt;: &lt;code&gt;--vacuum-size=&lt;/code&gt;, &lt;code&gt;--vacuum-time=&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>faster-whisper: Library libcublas.so.12 is not found or cannot be loaded (system has CUDA 13)</title>
    <link href="https://www.misc2.com/technotes/faster-whisper-libcublas-so-12-not-found-on-cuda-13/"/>
    <id>https://www.misc2.com/technotes/faster-whisper-libcublas-so-12-not-found-on-cuda-13/</id>
    <published>2026-09-10T00:00:00Z</published>
    <updated>2026-09-10T00:00:00Z</updated>
    <summary>faster-whisper fails at the first GPU compute with &quot;Library libcublas.so.12 is not found&quot; because the ctranslate2 wheel is built for CUDA 12 and the system has CUDA 13. Install nvidia-cublas-cu12 and nvidia-cudnn-cu12 into the venv and set LD_LIBRARY_PATH in a wrapper before Python starts.</summary>
    <category term="cuda"/><category term="python"/><category term="faster-whisper"/><category term="ctranslate2"/><category term="whisper"/><category term="nvidia"/><category term="arch"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;faster-whisper with &lt;code&gt;device=&amp;quot;cuda&amp;quot;&lt;/code&gt; dies at the first GPU computation with:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;Library libcublas.so.12 is not found or cannot be loaded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The system CUDA is version 13 and only has &lt;code&gt;libcublas.so.13&lt;/code&gt; (on Arch, in &lt;code&gt;/opt/cuda/lib64&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;A quick smoke test can hide the problem. A test on silence or a synthetic sine wave &lt;strong&gt;passes&lt;/strong&gt;
even with cuBLAS missing: voice activity detection (VAD) finds no speech, so the GPU encoder
never runs. Always smoke-test speech-to-text on real speech.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ctranslate2&lt;/code&gt; 4.7.1 wheel from PyPI is linked against &lt;strong&gt;CUDA 12&lt;/strong&gt;. It loads
&lt;code&gt;libcublas.so.12&lt;/code&gt; (and cuDNN for CUDA 12) at run time. A CUDA 13 system does not provide
those libraries, and the loader will not substitute &lt;code&gt;libcublas.so.13&lt;/code&gt; for a &lt;code&gt;.so.12&lt;/code&gt;
dependency.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Put the CUDA 12 runtime libraries inside the venv, so it does not depend on what the system
or another application happens to ship:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&amp;lt;venv&amp;gt;/bin/pip&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia-cublas-cu12&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia-cudnn-cu12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These wheels put the libraries under &lt;code&gt;site-packages/nvidia/cublas/lib&lt;/code&gt; and
&lt;code&gt;site-packages/nvidia/cudnn/lib&lt;/code&gt;. The dynamic loader still has to find them, so start Python
through a wrapper that sets &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;VENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;venv&amp;gt;
&lt;span class=&quot;nv&quot;&gt;SP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$VENV&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/lib/python3.11/site-packages&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/nvidia/cublas/lib:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/nvidia/cudnn/lib:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$VENV&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/python&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;transcribe.py&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting &lt;code&gt;os.environ[&amp;quot;LD_LIBRARY_PATH&amp;quot;]&lt;/code&gt; inside the Python script does not work: the dynamic
loader reads &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; when the process starts, so it has to be exported before
&lt;code&gt;exec&lt;/code&gt;. Adjust &lt;code&gt;python3.11&lt;/code&gt; in the path to your venv&#x27;s Python version.&lt;/p&gt;
&lt;p&gt;An earlier workaround pointed &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; at the CUDA 12 libraries bundled with another
application (ollama&#x27;s &lt;code&gt;cuda_v12&lt;/code&gt; directory). That works, but it breaks if that application is
removed or its bundled CUDA moves to version 13. The venv-local wheels avoid that.&lt;/p&gt;
&lt;p&gt;With this in place, sequential transcription with &lt;code&gt;large-v3&lt;/code&gt; ran at about &lt;strong&gt;31x realtime&lt;/strong&gt; on
an RTX 5080 (5.35 hours of audio in about 11 minutes of GPU time).&lt;/p&gt;
&lt;h3 id=&quot;related-crash-no-clip-timestamps-found-with-batchedinferencepipeline&quot;&gt;Related crash: &amp;quot;No clip timestamps found&amp;quot; with BatchedInferencePipeline &lt;a class=&quot;header-anchor&quot; href=&quot;#related-crash-no-clip-timestamps-found-with-batchedinferencepipeline&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the same bulk job, &lt;code&gt;BatchedInferencePipeline&lt;/code&gt; crashed on a file that was music only:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;RuntimeError: No clip timestamps found. Set &#x27;vad_filter&#x27; to True or provide &#x27;clip_timestamps&#x27;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The batched pipeline needs VAD to return at least one speech clip and raises when it returns
none. One music-only or silent file stops a whole batch. The sequential
&lt;code&gt;WhisperModel.transcribe()&lt;/code&gt; handles such files without raising, and at ~31x realtime it was
fast enough that the batched path was not worth it for bulk work:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;python&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;faster_whisper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WhisperModel&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WhisperModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;large-v3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cuda&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;float16&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transcribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ja&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vad_filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;n&quot;&gt;beam_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition_on_previous_text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;segments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# it is a generator; consume it before writing output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;condition_on_previous_text=False&lt;/code&gt; stops one hallucinated line from seeding the next.&lt;/p&gt;
&lt;p&gt;On music-only audio, &lt;code&gt;large-v3&lt;/code&gt; may still &amp;quot;transcribe&amp;quot; a stock filler phrase
(in Japanese, &lt;code&gt;ご視聴ありがとうございました&lt;/code&gt;, &amp;quot;thank you for watching&amp;quot;) once or twice and
nothing else. It looks like a transcript but is hallucination. A cheap check that worked for
flagging such files:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;python&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;uniq&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;segments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;no_speech&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uniq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For checking a whole batch without reading every file, characters per second of audio was a
useful health metric: conversational Japanese interviews landed in a tight 5.1–5.8 chars/s
band, and the outliers were real silence or music, not failures.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Claude Code: auth login succeeds in the browser, then &quot;not logged in&quot; (~/.claude owned by root)</title>
    <link href="https://www.misc2.com/technotes/claude-code-auth-login-not-logged-in-root-owned-dot-claude/"/>
    <id>https://www.misc2.com/technotes/claude-code-auth-login-not-logged-in-root-owned-dot-claude/</id>
    <published>2026-06-28T00:00:00Z</published>
    <updated>2026-06-28T00:00:00Z</updated>
    <summary>claude auth login completes in the browser, then Claude Code says it is not logged in and gives up after a few retries. ~/.claude was owned by root after claude had once been run as root with the user&#x27;s HOME, so the token file could not be written. chown the directory back to the user.</summary>
    <category term="claude-code"/><category term="linux"/><category term="arch"/><category term="permissions"/><category term="sudo"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;claude auth login&lt;/code&gt; as a normal user:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The browser OAuth flow completes and says the app is authorized.&lt;/li&gt;
&lt;li&gt;Claude Code then reports that it is &lt;strong&gt;not logged in&lt;/strong&gt;, retries about three times, and gives up.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;claude auth status&lt;/code&gt; shows:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;json&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;loggedIn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;authMethod&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;~/.claude/.credentials.json&lt;/code&gt; is ever created.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;~/.claude/&lt;/code&gt; (and &lt;code&gt;~/.claude/skills/&lt;/code&gt;) were owned by &lt;strong&gt;root&lt;/strong&gt;, mode 755:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;ls&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-ld&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;~/.claude&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;~/.claude/skills&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# owner shows as root instead of the user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code saves the OAuth token to &lt;code&gt;~/.claude/.credentials.json&lt;/code&gt;. The user cannot create
files in a root-owned directory, so the token is never written and every check reads
&amp;quot;logged out&amp;quot;. The login itself works; only saving it fails.&lt;/p&gt;
&lt;p&gt;The directory had been created by a single run of &lt;code&gt;claude&lt;/code&gt; &lt;strong&gt;as root with the user&#x27;s HOME&lt;/strong&gt;
during setup (a &lt;code&gt;sudo claude&lt;/code&gt;, or a skills install run as root). Nothing kept
re-breaking it; it was a one-off. The exact command that did it was not identified, only the
time window from the directory timestamps.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Give the directory back to the user, then log in again:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;sudo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;chown&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-R&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;user:user&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;~/.claude
claude&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;auth&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;login
claude&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;auth&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;status&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# should now report &quot;loggedIn&quot;: true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the &lt;code&gt;chown&lt;/code&gt;, &lt;code&gt;claude auth login&lt;/code&gt; saved the credentials normally. The same latent problem
was found and fixed on two other machines set up the same way.&lt;/p&gt;
&lt;p&gt;To avoid it: &lt;strong&gt;do not run &lt;code&gt;claude&lt;/code&gt; as root with a normal user&#x27;s HOME&lt;/strong&gt; (&lt;code&gt;sudo claude&lt;/code&gt;,
or root with &lt;code&gt;HOME&lt;/code&gt; pointing at a user&#x27;s home). It leaves &lt;code&gt;~/.claude&lt;/code&gt; owned
by root and silently breaks that user&#x27;s login and skill sync.&lt;/p&gt;
&lt;h2 id=&quot;unrelated-claude-command-not-found-in-some-shells&quot;&gt;Unrelated: &amp;quot;claude: command not found&amp;quot; in some shells &lt;a class=&quot;header-anchor&quot; href=&quot;#unrelated-claude-command-not-found-in-some-shells&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On one machine there was a second, unrelated symptom: typing &lt;code&gt;claude&lt;/code&gt; in an interactive shell
gave &lt;code&gt;command not found&lt;/code&gt;. Claude Code was installed with npm into &lt;code&gt;~/.npm-global&lt;/code&gt;, and
&lt;code&gt;~/.npm-global/bin&lt;/code&gt; was added to &lt;code&gt;PATH&lt;/code&gt; only in &lt;code&gt;~/.bash_profile&lt;/code&gt;, not in &lt;code&gt;~/.bashrc&lt;/code&gt;. Login
shells found &lt;code&gt;claude&lt;/code&gt;; non-login interactive shells did not. Fix: add it in &lt;code&gt;~/.bashrc&lt;/code&gt; too:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.npm-global/bin:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title>Default ACL shows #effective:--- on files an app rewrites with mode 0600 (certbot keys, Home Assistant auth)</title>
    <link href="https://www.misc2.com/technotes/default-acl-effective-none-on-files-written-with-mode-0600/"/>
    <id>https://www.misc2.com/technotes/default-acl-effective-none-on-files-written-with-mode-0600/</id>
    <published>2026-06-25T00:00:00Z</published>
    <updated>2026-06-25T00:00:00Z</updated>
    <summary>A default ACL granting a backup user read access worked, then broke after certbot issued a key or Home Assistant rewrote .storage/auth. Files created with mode 0600 get an ACL mask of ---, so the inherited entry shows #effective:---. Re-running setfacl -m recomputes the mask; run it from cron before each backup.</summary>
    <category term="linux"/><category term="acl"/><category term="permissions"/><category term="certbot"/><category term="home-assistant"/><category term="backup"/><category term="cron"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A nightly backup runs as an unprivileged backup user. Root-owned files were made readable to
that user with a &lt;strong&gt;default ACL&lt;/strong&gt; on their directories, and the backup worked for a few days.
Then it started reporting one error each night.&lt;/p&gt;
&lt;p&gt;On the Home Assistant machine (restic):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;error: open /var/lib/homeassistant/.storage/auth: permission denied
Warning: at least one source file could not be read
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the certbot machine (rsync), right after a new certificate was issued:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;rsync: [sender] send_files failed to open &quot;/etc/letsencrypt/archive/example.com/privkey1.pem&quot;: Permission denied (13)
rsync: [sender] send_files failed to open &quot;/etc/letsencrypt/keys/0062_key-certbot.pem&quot;: Permission denied (13)
rsync error: some files/attrs were not transferred (see previous errors) (code 23)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;getfacl&lt;/code&gt; on the failing file shows the named-user entry is still there, but masked out:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;user:backupuser:r-x	#effective:---
mask::---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only the newly written files were affected. Older files in the same directories (for example
Home Assistant&#x27;s &lt;code&gt;auth_provider.homeassistant&lt;/code&gt;, mode 0640) stayed readable.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a file has named-user or named-group ACL entries, it also has a &lt;strong&gt;mask&lt;/strong&gt; entry. The
effective permission of every named entry (and of the owning group entry) is its own
permission AND the mask. &lt;code&gt;getfacl&lt;/code&gt; prints &lt;code&gt;#effective:&lt;/code&gt; when the mask removes something.&lt;/p&gt;
&lt;p&gt;When a file is created in a directory that has a default ACL, &lt;code&gt;acl(5)&lt;/code&gt; (&amp;quot;Object creation and
default ACLs&amp;quot;) says:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the new file inherits the directory&#x27;s default ACL as its access ACL, and&lt;/li&gt;
&lt;li&gt;the entries that correspond to the file permission bits are then reduced to what the
&lt;code&gt;mode&lt;/code&gt; argument of &lt;code&gt;open()&lt;/code&gt;/&lt;code&gt;creat()&lt;/code&gt; allows.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the ACL has a mask, the &lt;strong&gt;group permission bits correspond to the mask entry&lt;/strong&gt;, not to the
owning-group entry (&lt;code&gt;acl(5)&lt;/code&gt;, &amp;quot;Correspondence between ACL entries and file permission bits&amp;quot;).
So the group bits of the create mode cap the mask. An application that creates its secrets
with mode &lt;code&gt;0600&lt;/code&gt; has group bits &lt;code&gt;---&lt;/code&gt;, so the new file gets &lt;code&gt;mask::---&lt;/code&gt;, and the inherited
&lt;code&gt;user:backupuser:r-x&lt;/code&gt; becomes &lt;code&gt;#effective:---&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same correspondence works in the other direction, so a later &lt;code&gt;chmod 600&lt;/code&gt; on a file with
an ACL also sets the mask to &lt;code&gt;---&lt;/code&gt;. (General ACL behaviour from the man page; in these two cases
the files were created or rewritten by the application at 0600.)&lt;/p&gt;
&lt;p&gt;That is why the default ACL looked like a fix and then stopped working:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Home Assistant&lt;/strong&gt; rewrites &lt;code&gt;.storage/auth&lt;/code&gt; (mode 0600) whenever auth or refresh tokens change.
The default ACL lasted until the next token rotation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;certbot&lt;/strong&gt; writes new private keys under &lt;code&gt;/etc/letsencrypt/archive/&amp;lt;name&amp;gt;/&lt;/code&gt; and
&lt;code&gt;/etc/letsencrypt/keys/&lt;/code&gt; at mode 0600. Issuing a certificate for a new name produced two
unreadable files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A default ACL alone can never give a durable grant on files an application (re)writes at mode
0600.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;setfacl -m&lt;/code&gt; recalculates the mask by default. &lt;code&gt;setfacl(1)&lt;/code&gt;: the mask is set to &amp;quot;the union of all
permissions of the owning group, and all named user and group entries&amp;quot;, unless you give a mask
entry explicitly or use &lt;code&gt;-n&lt;/code&gt;. So re-applying the grant restores &lt;code&gt;mask::r-x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Immediate fix, as root:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;setfacl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-R&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-m&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;u:backupuser:rX&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/var/lib/homeassistant/.storage
setfacl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-R&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-m&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;u:backupuser:rX&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/etc/letsencrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(&lt;code&gt;X&lt;/code&gt; gives execute only to directories and to files that already have execute for someone.)&lt;/p&gt;
&lt;p&gt;Durable fix: re-apply it from root&#x27;s cron a few minutes before the backup runs, for example
&lt;code&gt;/etc/cron.d/backup-acl-refresh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;55 2 * * * root setfacl -R -m u:backupuser:rX /var/lib/homeassistant/.storage 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with the backup at 03:00, and the same pattern with &lt;code&gt;/etc/letsencrypt&lt;/code&gt; on the certbot machine.&lt;/p&gt;
&lt;p&gt;Verified: on the Home Assistant machine a manual backup re-run then finished with no errors; on
the certbot machine all &lt;code&gt;*.pem&lt;/code&gt; files were readable by the backup user and an rsync dry-run was
clean.&lt;/p&gt;
&lt;p&gt;Caveats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the application rewrites the file between the cron job and the backup, that night still
fails. Home Assistant is idle overnight, so this was accepted as rare.&lt;/li&gt;
&lt;li&gt;The recalculated mask also re-enables whatever the &lt;strong&gt;owning-group&lt;/strong&gt; entry grants (the mask is
the union of that entry and the named entries). Check &lt;code&gt;getfacl&lt;/code&gt; on a key file after the fix if
the owning group should not read your private keys. This follows from &lt;code&gt;setfacl(1)&lt;/code&gt;; it was not
checked separately in these cases.&lt;/li&gt;
&lt;li&gt;The alternative is to not need the ACL at all: read the files as root (or have a root job copy
them) for the backup.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;acl(5)&lt;/code&gt;: &amp;quot;Correspondence between ACL entries and file permission bits&amp;quot; and &amp;quot;Object creation and
default ACLs&amp;quot;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setfacl(1)&lt;/code&gt;: &lt;code&gt;-n, --no-mask&lt;/code&gt; and &lt;code&gt;--mask&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Black screen after restarting X: NVIDIA: Failed to initialize the NVIDIA kernel module (kernel upgraded, not rebooted)</title>
    <link href="https://www.misc2.com/technotes/x-black-screen-nvidia-failed-to-initialize-kernel-module-after-kernel-upgrade/"/>
    <id>https://www.misc2.com/technotes/x-black-screen-nvidia-failed-to-initialize-kernel-module-after-kernel-upgrade/</id>
    <published>2026-06-19T00:00:00Z</published>
    <updated>2026-06-19T00:00:00Z</updated>
    <summary>After a kernel and NVIDIA driver upgrade without a reboot, restarting X gave a black screen and &quot;Failed to initialize the NVIDIA kernel module&quot;. The running kernel had only the old module loaded. Build the module for the running kernel with DKMS, swap the modules, recreate /dev/dri, then reboot properly.</summary>
    <category term="nvidia"/><category term="arch"/><category term="linux"/><category term="xorg"/><category term="dkms"/><category term="kernel"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A GPU application crashed X on the desktop. Restarting X then gave a permanent black screen.
The display manager log showed X dying straight away:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;Failed to read display number from pipe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The real error was in the Xorg log (&lt;code&gt;/var/log/Xorg.0.log&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;(EE) NVIDIA: Failed to initialize the NVIDIA kernel module
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;dmesg&lt;/code&gt; had an &lt;code&gt;API mismatch&lt;/code&gt; message: the client (the Xorg driver) was 610.43.02 and the
kernel module was 590.48.01.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The kernel and the NVIDIA driver had been upgraded with pacman, but the machine had not been
rebooted:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;uname&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-r&lt;span class=&quot;w&quot;&gt;                          &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# 6.18.13-arch1-1   (running)&lt;/span&gt;
pacman&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-Q&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;linux&lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# linux 7.0.12.arch1-1  (installed)&lt;/span&gt;
cat&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/proc/driver/nvidia/version&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# 590.48.01  (module in memory)&lt;/span&gt;
dkms&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;status&lt;span class=&quot;w&quot;&gt;                       &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# nvidia 610.43.02 built for 7.0.12 only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;DKMS built &lt;code&gt;nvidia-open-dkms 610.43.02&lt;/code&gt; only for the new kernel, 7.0.12. There was no
610 module for the running kernel, 6.18.13.&lt;/li&gt;
&lt;li&gt;The old 590.48.01 module was still loaded. The new 610.43.02 Xorg driver cannot talk to it,
so X fails at startup.&lt;/li&gt;
&lt;li&gt;The old module could not simply be unloaded: a second Xorg server (Chrome Remote Desktop
runs its own, here on display &lt;code&gt;:20&lt;/code&gt;) still had it open. &lt;code&gt;fuser /dev/nvidia* /dev/dri/*&lt;/code&gt;
showed that process.&lt;/li&gt;
&lt;li&gt;Even after that server was stopped, &lt;code&gt;nvidia_drm&lt;/code&gt; still had kernel-internal DRM references
(&lt;code&gt;cat /sys/module/nvidia_drm/refcnt&lt;/code&gt; showed &lt;code&gt;9&lt;/code&gt;), so a normal &lt;code&gt;rmmod&lt;/code&gt; failed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The simple fix is to reboot into the new kernel. The steps below got a working display back
on the old kernel without a reboot, then prepared a clean reboot.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;live-recovery-no-reboot&quot;&gt;Live recovery (no reboot) &lt;a class=&quot;header-anchor&quot; href=&quot;#live-recovery-no-reboot&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run as root. Replace the versions with yours.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the headers for the &lt;strong&gt;running&lt;/strong&gt; kernel (they were still in the pacman cache):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;pacman&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-U&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/var/cache/pacman/pkg/linux-headers-&amp;lt;running-kernel-version&amp;gt;-x86_64.pkg.tar.zst
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rebuild the module database for the running kernel (the upgrade had removed its
&lt;code&gt;kernel/&lt;/code&gt; module tree):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;depmod&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-a&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;running-kernel-version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the new driver for the running kernel. The DKMS source is named &lt;code&gt;nvidia&lt;/code&gt;, even when
the installed package is &lt;code&gt;nvidia-open-dkms&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;dkms&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia/&amp;lt;driver-version&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-k&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;running-kernel-version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Stop everything that holds the module open (here, Chrome Remote Desktop&#x27;s Xorg):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;stop&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;chrome-remote-desktop@&amp;lt;user&amp;gt;.service
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unload the old modules. &lt;code&gt;nvidia_drm&lt;/code&gt; needed a forced removal because of the stuck
references:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;rmmod&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-f&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia_drm
rmmod&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia_modeset&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Load the new modules and check the version:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;modprobe&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-a&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia_modeset&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia_drm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nvidia_uvm
cat&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/proc/driver/nvidia/version&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# must show the new version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The forced &lt;code&gt;rmmod&lt;/code&gt; deleted &lt;code&gt;/dev/dri/&lt;/code&gt;. Re-add the GPU&#x27;s PCI device so udev recreates it
(find the address with &lt;code&gt;lspci -D | grep -i nvidia&lt;/code&gt;, e.g. &lt;code&gt;0000:02:00.0&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;udevadm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;trigger&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--action&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;add&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/sys/bus/pci/devices/&amp;lt;gpu-pci-address&amp;gt;
udevadm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;settle
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the display manager, then Chrome Remote Desktop:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;start&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;lightdm&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# or sddm&lt;/span&gt;
systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;start&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;chrome-remote-desktop@&amp;lt;user&amp;gt;.service
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;modprobe&lt;/code&gt; does nothing if a module of that name is already loaded, even an old version.
Always check &lt;code&gt;/proc/driver/nvidia/version&lt;/code&gt; after loading.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;modprobe&lt;/code&gt; with several module names needs &lt;code&gt;-a&lt;/code&gt;. Without it, the names after the first are
passed as parameters to the first module. (Our original notes left out &lt;code&gt;-a&lt;/code&gt;; the version
check in step 6 is what showed the right module was loaded.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rmmod -f&lt;/code&gt; is a last resort. It was used here only because X was already dead and nothing
else would release &lt;code&gt;nvidia_drm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Start the main display manager &lt;strong&gt;before&lt;/strong&gt; restarting Chrome Remote Desktop. Its Xorg holds
the module and, if it comes up first, can take over the user session.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This recovery was only partly clean. X came back, but the desktop session started on the
remote desktop&#x27;s display instead of the physical one. Two settings caused that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/.dmrc&lt;/code&gt; still had &lt;code&gt;Session=xfce&lt;/code&gt; from an old desktop switch, so LightDM started a session
that exited at once, leaving only a cursor on the physical screen.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReuseSession=true&lt;/code&gt; in the SDDM config (SDDM was used during the recovery) made it attach to
the existing remote session instead of starting a new one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a stopgap on the physical console: &lt;code&gt;startx /usr/bin/startlxqt -- :0 vt2&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;before-rebooting-into-the-new-kernel&quot;&gt;Before rebooting into the new kernel &lt;a class=&quot;header-anchor&quot; href=&quot;#before-rebooting-into-the-new-kernel&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Step 1 installs the &lt;strong&gt;old&lt;/strong&gt; kernel&#x27;s headers, which replaces the new kernel&#x27;s headers. DKMS
then had the driver for the new kernel only &amp;quot;added&amp;quot;, not &amp;quot;installed&amp;quot;. Put the new headers
back and check before rebooting:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;pacman&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-U&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/var/cache/pacman/pkg/linux-headers-&amp;lt;new-kernel-version&amp;gt;-x86_64.pkg.tar.zst
dkms&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;status&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-k&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;new-kernel-version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The DKMS pacman hook rebuilt the module when the headers were installed. &lt;code&gt;dkms status&lt;/code&gt; should
then show &lt;code&gt;nvidia/&amp;lt;driver-version&amp;gt;, &amp;lt;new-kernel-version&amp;gt;, x86_64: installed&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Also fix the default session in &lt;code&gt;~/.dmrc&lt;/code&gt; (here &lt;code&gt;Session=xfce&lt;/code&gt; → &lt;code&gt;Session=lxqt&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;After the reboot into the new kernel, the journal had no NVIDIA errors and
&lt;code&gt;systemctl --failed&lt;/code&gt; listed no units.&lt;/p&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;journalctl -u lightdm -u sddm&lt;/code&gt; showed X exiting at once; &lt;code&gt;/var/log/Xorg.0.log&lt;/code&gt; gave the real
error.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uname -r&lt;/code&gt; against &lt;code&gt;pacman -Q linux&lt;/code&gt; showed the pending reboot.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/driver/nvidia/version&lt;/code&gt; and &lt;code&gt;dkms status&lt;/code&gt; showed the loaded module was older than the
userspace driver, with no matching build for the running kernel.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fuser /dev/nvidia* /dev/dri/*&lt;/code&gt; found the second Xorg holding the module;
&lt;code&gt;/sys/module/nvidia_drm/refcnt&lt;/code&gt; showed the leftover references after it stopped.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ls /tmp/.X*-lock&lt;/code&gt; and &lt;code&gt;ss -xl | grep X11&lt;/code&gt; listed the running X displays (&lt;code&gt;:0&lt;/code&gt; and &lt;code&gt;:20&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The lesson: after a pacman upgrade that touches the kernel or the NVIDIA driver, reboot before
restarting X.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>M5Stack Cardputer-ADV plays a loud constant whine over all audio (ES8311, Arduino M5Unified)</title>
    <link href="https://www.misc2.com/technotes/m5stack-cardputer-adv-es8311-loud-whine-over-audio/"/>
    <id>https://www.misc2.com/technotes/m5stack-cardputer-adv-es8311-loud-whine-over-audio/</id>
    <published>2026-06-14T00:00:00Z</published>
    <updated>2026-06-14T00:00:00Z</updated>
    <summary>On a Cardputer-ADV with custom Arduino firmware, every sound (test tone or MP3) had a loud constant whine on top. The speaker ran a mono I2S frame, so BCLK was half what M5Unified&#x27;s fixed ES8311 clock divider expects. Setting spk_cfg.stereo = true fixed it.</summary>
    <category term="m5stack"/><category term="cardputer"/><category term="es8311"/><category term="esp32"/><category term="arduino"/><category term="audio"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Custom Arduino firmware on an M5Stack &lt;strong&gt;Cardputer-ADV&lt;/strong&gt; (an MP3 player sketch built on
M5Cardputer / M5Unified and ESP8266Audio) produced a &lt;strong&gt;loud, constant whine over all
audio&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the same with a generated test tone and with MP3 playback,&lt;/li&gt;
&lt;li&gt;also when running on battery,&lt;/li&gt;
&lt;li&gt;at every sample rate tried.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The pitch of the actual audio was correct; only the added tone was wrong. The stock UIFlow
firmware plays through the same ES8311 codec cleanly, which showed it was a configuration
problem, not hardware.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A clock mismatch between the I2S frame and the codec&#x27;s clock divider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The speaker was configured &lt;strong&gt;mono&lt;/strong&gt;, so the I2S frame was 16 bits per sample period:
BCLK = fs × 16 = &lt;strong&gt;768 kHz&lt;/strong&gt; at 48 kHz.&lt;/li&gt;
&lt;li&gt;M5Unified initialises the ES8311 with a &lt;strong&gt;fixed clock divider&lt;/strong&gt; (register &lt;code&gt;0x02 = 0x18&lt;/code&gt;)
that assumes &lt;strong&gt;BCLK = 1.536 MHz&lt;/strong&gt; (a 32-bit stereo frame).&lt;/li&gt;
&lt;li&gt;With BCLK at half the expected rate, the DAC master clock ended up about &lt;strong&gt;2× off&lt;/strong&gt;, which produced the constant tone. The audio pitch stayed right because
LRCK still locked the sample rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fix below confirms the mechanism. Register-level changes did &lt;strong&gt;not&lt;/strong&gt; help: reprogramming
the ES8311 oversampling (OSR) and bias settings, and even a full ESP-ADF-style codec init,
left the whine in place. It took about a dozen reflashes to find.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Configure the speaker as &lt;strong&gt;stereo&lt;/strong&gt; before starting it. That gives a 32-bit frame and
BCLK = 1.536 MHz, which matches the divider:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;cpp&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spk_cfg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M5Cardputer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Speaker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spk_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stereo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 32-bit I2S frame -&amp;gt; BCLK 1.536 MHz at 48 kHz&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;M5Cardputer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Speaker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spk_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;M5Cardputer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Speaker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;spk_cfg.stereo = true&lt;/code&gt; was the whole fix; no codec registers needed changing. The
&lt;code&gt;config()&lt;/code&gt; read-modify-write lines are the usual M5Unified pattern; the line that matters
is setting &lt;code&gt;stereo&lt;/code&gt; before &lt;code&gt;Speaker.begin()&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ESP32-S3 without PSRAM: HTTPS fetch returns 0 bytes or connect() fails &quot;connection refused&quot; (TLS out of memory)</title>
    <link href="https://www.misc2.com/technotes/esp32-s3-no-psram-tls-out-of-memory-connection-refused/"/>
    <id>https://www.misc2.com/technotes/esp32-s3-no-psram-tls-out-of-memory-connection-refused/</id>
    <published>2026-06-14T00:00:00Z</published>
    <updated>2026-06-14T00:00:00Z</updated>
    <summary>On a PSRAM-less ESP32-S3 (M5Stack Cardputer-ADV) an HTTPS fetch returned 0 bytes and later TLS connects failed with &quot;connection refused&quot;. mbedTLS could not get a large enough contiguous heap block. Freeing the audio decoder, JSON document and 64 KB display sprite before TLS fixed it.</summary>
    <category term="esp32"/><category term="arduino"/><category term="wifi"/><category term="m5stack"/><category term="cardputer"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An Arduino MP3 player sketch on an M5Stack Cardputer-ADV fetches a JSON feed over HTTPS,
then downloads several files over HTTPS to the microSD card. On this board:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the feed fetch &lt;strong&gt;returned 0 bytes&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;once the feed had been parsed, the per-file downloads failed because &lt;code&gt;client.connect()&lt;/code&gt;
reported &lt;strong&gt;&amp;quot;connection refused&amp;quot;&lt;/strong&gt;, although the server was fine.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing was wrong with the network or the server. Both were memory failures that do not
look like memory failures. (The &amp;quot;connection refused&amp;quot; text is what the sketch&#x27;s connect
failure reported; the exact log line was not recorded.)&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Cardputer-ADV&#x27;s ESP32-S3 has &lt;strong&gt;no PSRAM&lt;/strong&gt;, only about 320 KB of internal SRAM, and a
TLS handshake needs large contiguous heap blocks. With the audio decoder, the audio output
object, a parsed JSON document and a 64 KB display sprite all allocated, only about
&lt;strong&gt;10 KB contiguous&lt;/strong&gt; was left after the mbedTLS handshake, and new TLS connections could not
get the buffers they needed.&lt;/p&gt;
&lt;p&gt;The number that settled it was the &lt;strong&gt;largest free heap block&lt;/strong&gt; before connecting: about
&lt;strong&gt;33 KB → TLS fails&lt;/strong&gt;, about &lt;strong&gt;64 KB → TLS works&lt;/strong&gt;. To watch it yourself, log
&lt;code&gt;heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)&lt;/code&gt; (or &lt;code&gt;ESP.getMaxAllocHeap()&lt;/code&gt;) just
before &lt;code&gt;client.connect()&lt;/code&gt;; total free heap is misleading here because fragmentation is
the problem.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Free the big allocations before any TLS work, and keep large data out of RAM:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Free the MP3 decoder and the audio output object&lt;/strong&gt; before starting TLS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stream the response to a temporary file on SD&lt;/strong&gt;, then parse the JSON from the file,
instead of reading the whole body into RAM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free the JSON document&lt;/strong&gt; before the per-file downloads. Without this step,
&lt;code&gt;client.connect()&lt;/code&gt; fails with &amp;quot;connection refused&amp;quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delete the 64 KB display sprite&lt;/strong&gt; for the duration of Wi-Fi/TLS. It turned out to be
the single largest allocation: once the app grew a little (more globals), steps 1–3
alone were no longer enough. While the sprite is gone, draw status text straight to
the display.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;cpp&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sprite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deleteSprite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// M5Canvas back buffer, ~64 KB&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ... connect, download, write to SD; draw progress directly on the display ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ESP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// come back up clean and recreate the sprite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restarting at the end of the sync is a simple way to get every freed object (decoder,
sprite) back in a known state. With all four steps, the largest free block went from about
33 KB to about 64 KB and the HTTPS fetches and downloads worked.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ESP32-S3 Arduino Wi-Fi HTTP downloads crawl at about 1 KB/s</title>
    <link href="https://www.misc2.com/technotes/esp32-arduino-wifi-http-download-1-kb-s/"/>
    <id>https://www.misc2.com/technotes/esp32-arduino-wifi-http-download-1-kb-s/</id>
    <published>2026-06-14T00:00:00Z</published>
    <updated>2026-06-14T00:00:00Z</updated>
    <summary>HTTP(S) downloads to SD from an ESP32-S3 Arduino sketch (M5Stack Cardputer-ADV) ran at about 1 KB/s. The cause was Wi-Fi modem power-save. Calling WiFi.setSleep(false) after connecting raised it to about 85 KB/s.</summary>
    <category term="esp32"/><category term="arduino"/><category term="wifi"/><category term="m5stack"/><category term="cardputer"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An Arduino MP3 player sketch on an M5Stack Cardputer-ADV downloads MP3 files over Wi-Fi
and writes them to the microSD card. Throughput was about &lt;strong&gt;1 KB/s&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Wi-Fi &lt;strong&gt;modem power-save&lt;/strong&gt;. Turning it off was the only change needed, which is how it
was identified.&lt;/p&gt;
&lt;p&gt;Background (general ESP32 behaviour, not separately measured here): in station mode the
ESP32 Wi-Fi driver uses modem sleep by default, so the radio dozes between access-point
beacons. That is fine for occasional small requests but throttles a long bulk download.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Turn modem sleep off once the station is up:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;cpp&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cpf&quot;&gt;&amp;lt;WiFi.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIFI_STA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-ssid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-wifi-password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WL_CONNECTED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setSleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// disable modem power-save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this one change, downloads went from about 1 KB/s to about &lt;strong&gt;85 KB/s&lt;/strong&gt;, and the files
were verified byte for byte.&lt;/p&gt;
&lt;p&gt;Caveat (not measured here): with sleep off the radio stays awake, so expect higher current
draw while Wi-Fi is on. On a battery device, turn Wi-Fi off when the transfer is done.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ESPHome device reboots every 15 minutes: &quot;[E][api] No clients; rebooting&quot;</title>
    <link href="https://www.misc2.com/technotes/esphome-no-clients-rebooting-every-15-minutes/"/>
    <id>https://www.misc2.com/technotes/esphome-no-clients-rebooting-every-15-minutes/</id>
    <published>2026-06-06T00:00:00Z</published>
    <updated>2026-06-06T00:00:00Z</updated>
    <summary>An ESPHome node with no Home Assistant connected rebooted about every 15 minutes, logging &quot;No clients; rebooting&quot;. The api component has its own reboot_timeout (default 15min) that fires when no API client connects. Setting reboot_timeout to 0s under api: stopped the reboots.</summary>
    <category term="esphome"/><category term="esp8266"/><category term="home-assistant"/><category term="iot"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An ESP8266 running ESPHome rebooted roughly every 15 minutes. It joined Wi-Fi normally,
then restarted. This happened while it was being developed on the bench, before any Home Assistant instance was connecting to it.&lt;/p&gt;
&lt;p&gt;The log line just before each restart:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;[E][api:127]: No clients; rebooting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the device was on a test network at the time, the first suspect was Wi-Fi. It was
not Wi-Fi.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The native &lt;code&gt;api:&lt;/code&gt; component has its &lt;strong&gt;own&lt;/strong&gt; &lt;code&gt;reboot_timeout&lt;/code&gt;, separate from the one in
&lt;code&gt;wifi:&lt;/code&gt;. If no API client (normally Home Assistant) connects within that time, the node
reboots on purpose. The ESPHome docs describe it as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The amount of time to wait before rebooting when no client connects to the API. This is
needed because sometimes the low level ESP functions report that the ESP is connected to
the network, when in fact it is not - only a full reboot fixes it. Can be disabled by
setting this to &lt;code&gt;0s&lt;/code&gt;. Defaults to &lt;code&gt;15min&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So any ESPHome node that includes &lt;code&gt;api:&lt;/code&gt; but that no Home Assistant ever talks to (bench
testing, a device you control only through &lt;code&gt;web_server:&lt;/code&gt;, a network Home Assistant can&#x27;t
reach) will restart every 15 minutes, and the log says so in plain words.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Set the API reboot timeout to &lt;code&gt;0s&lt;/code&gt; (disabled):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;yaml&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;encryption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;!secret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;api_encryption_key&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;reboot_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;0s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reflashing, the node stayed up (no further reboots on the bench).&lt;/p&gt;
&lt;p&gt;On this device &lt;code&gt;reboot_timeout: 0s&lt;/code&gt; was also set under &lt;code&gt;wifi:&lt;/code&gt;, so it keeps retrying
Wi-Fi forever instead of rebooting:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;yaml&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;wifi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-ssid&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-wifi-password&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;reboot_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;0s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That second setting was a deliberate choice for this device, not part of the 15-minute
fix: the reboot loop was caused by &lt;code&gt;api:&lt;/code&gt; alone.&lt;/p&gt;
&lt;p&gt;Caveat: the API timeout exists for a reason. Once Home Assistant is connecting reliably,
you may want to put a non-zero &lt;code&gt;reboot_timeout&lt;/code&gt; back under &lt;code&gt;api:&lt;/code&gt; so a node whose network
stack has silently wedged recovers on its own.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ESPHome docs, &lt;a href=&quot;https://esphome.io/components/api.html&quot;&gt;Native API Component&lt;/a&gt;, &lt;code&gt;reboot_timeout&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>CUPS &quot;Unable to locate printer *.local&quot; even though avahi-resolve finds the printer</title>
    <link href="https://www.misc2.com/technotes/cups-unable-to-locate-printer-local-nsswitch-mdns/"/>
    <id>https://www.misc2.com/technotes/cups-unable-to-locate-printer-local-nsswitch-mdns/</id>
    <published>2026-05-16T00:00:00Z</published>
    <updated>2026-05-16T00:00:00Z</updated>
    <summary>CUPS jobs to an ipp://printer.local queue never printed and lpstat showed &quot;Unable to locate printer&quot;. Avahi could see the printer, but /etc/nsswitch.conf had no mdns entry, so normal name lookups for .local failed. Adding mdns4_minimal [NOTFOUND=return] to the hosts line fixed it.</summary>
    <category term="cups"/><category term="printers"/><category term="mdns"/><category term="avahi"/><category term="nss-mdns"/><category term="arch"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A network printer had been set up in CUPS using its mDNS hostname. The queue accepted jobs,
but nothing printed and jobs piled up. &lt;code&gt;lpstat -t&lt;/code&gt; showed:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;device for Office_Printer: ipp://printer.local:631/ipp/print
Unable to locate printer &quot;printer.local&quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the same time, Avahi had no trouble finding the printer:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;avahi-resolve&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-n&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;printer.local&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# returns the printer&#x27;s address&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But ordinary name resolution did not:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;getent&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;hosts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;printer.local&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# prints nothing&lt;/span&gt;
ping&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;printer.local&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Name or service not known&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CUPS resolves the queue&#x27;s hostname through the normal glibc resolver (NSS), not by asking
Avahi directly. Whether NSS knows about &lt;code&gt;.local&lt;/code&gt; names is decided by the &lt;code&gt;hosts:&lt;/code&gt; line in
&lt;code&gt;/etc/nsswitch.conf&lt;/code&gt;, which here was:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;hosts: files dns myhostname
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is no &lt;code&gt;mdns&lt;/code&gt; module in that list, so &lt;code&gt;.local&lt;/code&gt; lookups went to &lt;code&gt;files&lt;/code&gt; and &lt;code&gt;dns&lt;/code&gt;, both
of which fail. The &lt;code&gt;nss-mdns&lt;/code&gt; package (which provides the &lt;code&gt;mdns*&lt;/code&gt; NSS modules) was already installed; it
just was not enabled in &lt;code&gt;nsswitch.conf&lt;/code&gt;. On Arch it is only an &lt;em&gt;optional&lt;/em&gt; dependency of
avahi, so on another machine you may need &lt;code&gt;pacman -S nss-mdns&lt;/code&gt; first.&lt;/p&gt;
&lt;p&gt;The difference between &lt;code&gt;avahi-resolve&lt;/code&gt; (works) and &lt;code&gt;getent hosts&lt;/code&gt; (fails) is the tell:
&lt;code&gt;avahi-resolve&lt;/code&gt; talks to avahi-daemon directly, &lt;code&gt;getent&lt;/code&gt; goes through NSS the way CUPS does.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;mdns4_minimal [NOTFOUND=return]&lt;/code&gt; to the &lt;code&gt;hosts:&lt;/code&gt; line, before &lt;code&gt;dns&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;sudo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;sed&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-i.bak&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;s|^hosts:.*|hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname|&#x27;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/etc/nsswitch.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your &lt;code&gt;hosts:&lt;/code&gt; line has other entries (for example &lt;code&gt;resolve&lt;/code&gt; or &lt;code&gt;mymachines&lt;/code&gt;), edit it by
hand instead of replacing the whole line; the point is to insert
&lt;code&gt;mdns4_minimal [NOTFOUND=return]&lt;/code&gt; before &lt;code&gt;dns&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Check:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;getent&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;hosts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;printer.local&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# now prints the printer&#x27;s address&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No service restart is needed: NSS reads &lt;code&gt;nsswitch.conf&lt;/code&gt; on each lookup, and CUPS picked up
the change on the next job.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[NOTFOUND=return]&lt;/code&gt; stops the lookup when mDNS says a &lt;code&gt;.local&lt;/code&gt; name does not exist, instead
of falling through to DNS. That avoids slow upstream DNS queries for names that will never
resolve there.&lt;/p&gt;
&lt;p&gt;The same fix applies to any &lt;code&gt;.local&lt;/code&gt; device on the machine (AirPlay receivers, other
Bonjour/mDNS hosts), not only printers.&lt;/p&gt;
&lt;h3 id=&quot;do-not-cancel-the-stuck-jobs&quot;&gt;Do not cancel the stuck jobs &lt;a class=&quot;header-anchor&quot; href=&quot;#do-not-cancel-the-stuck-jobs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Jobs stuck on a name-resolution failure are real prints. Once the hostname resolves, CUPS
retries them and they print. If you cancel them while diagnosing (&lt;code&gt;cancel -a&lt;/code&gt;), they have
to be printed again. We made this mistake once; leave the queue alone and let it flush.&lt;/p&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;lpstat&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-t&lt;span class=&quot;w&quot;&gt;                          &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# queues and the last error per queue&lt;/span&gt;
avahi-resolve&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-n&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;printer.local&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# what Avahi sees&lt;/span&gt;
getent&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;hosts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;printer.local&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# what NSS (and CUPS) sees: the gap&lt;/span&gt;
avahi-browse&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-art&lt;span class=&quot;w&quot;&gt;                  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# list mDNS services on the network&lt;/span&gt;
grep&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;^hosts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/etc/nsswitch.conf&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# look for mdns4_minimal&lt;/span&gt;
pacman&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-Q&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;nss-mdns&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# confirm the NSS module is installed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Arch Wiki, &lt;a href=&quot;https://wiki.archlinux.org/title/Avahi#Hostname_resolution&quot;&gt;Avahi: Hostname resolution&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>systemd user service not started at boot: &quot;Found ordering cycle ... Job deleted to break ordering cycle&quot;</title>
    <link href="https://www.misc2.com/technotes/systemd-user-service-found-ordering-cycle-after-default-target/"/>
    <id>https://www.misc2.com/technotes/systemd-user-service-found-ordering-cycle-after-default-target/</id>
    <published>2026-04-24T00:00:00Z</published>
    <updated>2026-04-24T00:00:00Z</updated>
    <summary>A systemd user service was inactive after every boot. The journal showed default.target &quot;Found ordering cycle&quot; and deleted its start job. A dependency was WantedBy=default.target and also After=default.target; removing the After= line fixed it.</summary>
    <category term="systemd"/><category term="linux"/><category term="systemd-user"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A web front end, run as a systemd &lt;strong&gt;user&lt;/strong&gt; service, was down after a reboot. The reverse proxy
in front of it returned 502 and logged &lt;code&gt;connection refused&lt;/code&gt; to the local port. The unit was
&lt;code&gt;inactive (dead)&lt;/code&gt; since boot, and nothing had tried to start it. The user journal from boot
had this (unit names made generic):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;default.target: Found ordering cycle: myapp-web.service/start
  after myappd.service/start after default.target/start
  - after myapp-web.service
default.target: Job myapp-web.service/start deleted to break ordering cycle
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Two user units:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;myappd.service&lt;/code&gt;: the daemon. &lt;code&gt;WantedBy=default.target&lt;/code&gt;, &lt;strong&gt;and &lt;code&gt;After=default.target&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myapp-web.service&lt;/code&gt;: the web front end. &lt;code&gt;WantedBy=default.target&lt;/code&gt;, &lt;code&gt;Requires=&lt;/code&gt; and
&lt;code&gt;After=myappd.service&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;WantedBy=default.target&lt;/code&gt; makes the target &lt;em&gt;want&lt;/em&gt; the unit. A target unit also automatically
adds &lt;code&gt;After=&lt;/code&gt; for everything it &lt;code&gt;Wants=&lt;/code&gt; or &lt;code&gt;Requires=&lt;/code&gt; (&lt;code&gt;systemd.target(5)&lt;/code&gt;), so
&lt;code&gt;default.target&lt;/code&gt; is ordered after &lt;code&gt;myapp-web.service&lt;/code&gt;. With the explicit lines that gives:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;default.target  after  myapp-web.service  after  myappd.service  after  default.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is a loop. systemd breaks ordering cycles at boot by deleting one of the jobs in it,
here the start job for &lt;code&gt;myapp-web.service&lt;/code&gt;, so the web front end never started.&lt;/p&gt;
&lt;p&gt;A unit that is &lt;code&gt;WantedBy=&lt;/code&gt; a target must not also be &lt;code&gt;After=&lt;/code&gt; that same target: the target only
counts as reached once the units it wants are up.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remove &lt;code&gt;After=default.target&lt;/code&gt; from the daemon&#x27;s unit (&lt;code&gt;~/.config/systemd/user/myappd.service&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;ini&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;[Unit]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;myapp daemon&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[Service]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;simple&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/path/to/myappd&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;on-failure&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;RestartSec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;5&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[Install]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;default.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--user&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;daemon-reload
systemctl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--user&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;start&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;myapp-web.service&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# pulls in myappd via Requires=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both units came up, the local port answered, and the proxy stopped returning 502.&lt;/p&gt;
&lt;p&gt;If a user service really needs ordering, order it after a more specific unit, not after the
target that wants it. Note that services with default dependencies are already ordered
&lt;code&gt;After=basic.target&lt;/code&gt; (&lt;code&gt;systemd.service(5)&lt;/code&gt;), so you do not need to add that yourself.&lt;/p&gt;
&lt;h2 id=&quot;how-it-was-found&quot;&gt;How it was found &lt;a class=&quot;header-anchor&quot; href=&quot;#how-it-was-found&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The proxy logged &lt;code&gt;connection refused&lt;/code&gt; to the local port, so the back end was down rather than
slow. &lt;code&gt;systemctl --user status myapp-web.service&lt;/code&gt; showed &lt;code&gt;inactive (dead)&lt;/code&gt; since boot, and
the user journal for that boot showed the ordering-cycle message above.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;systemd.target(5)&lt;/code&gt;: automatic &lt;code&gt;After=&lt;/code&gt; dependencies for units a target wants.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;systemd.service(5)&lt;/code&gt;: implicit dependencies of service units.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Chromium &quot;Show in folder&quot; fails with &#x27;&quot;.&quot; is not a valid folder&#x27; (opens Baobab)</title>
    <link href="https://www.misc2.com/technotes/chromium-show-in-folder-not-a-valid-folder-baobab/"/>
    <id>https://www.misc2.com/technotes/chromium-show-in-folder-not-a-valid-folder-baobab/</id>
    <published>2026-04-14T00:00:00Z</published>
    <updated>2026-04-14T00:00:00Z</updated>
    <summary>Clicking the folder icon in Chromium&#x27;s downloads panel gave &#x27;&quot;.&quot; is not a valid folder&#x27;. Baobab (GNOME Disk Usage Analyzer) was the default handler for inode/directory, so xdg-open launched it. Making a file manager the default in ~/.config/mimeapps.list fixed it.</summary>
    <category term="chromium"/><category term="xdg"/><category term="mime"/><category term="baobab"/><category term="lxqt"/><category term="arch"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Chromium&#x27;s downloads panel, clicking the folder icon next to a downloaded file (&amp;quot;Show in
folder&amp;quot;) did not open a file manager. Instead this error appeared:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;&quot;.&quot; is not a valid folder
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root cause &lt;a class=&quot;header-anchor&quot; href=&quot;#root-cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Linux, Chromium hands &amp;quot;show in folder&amp;quot; to &lt;code&gt;xdg-open&lt;/code&gt;, which picks the default application
for the MIME type &lt;code&gt;inode/directory&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Baobab (GNOME Disk Usage Analyzer) declares that MIME type in its desktop file,
&lt;code&gt;/usr/share/applications/org.gnome.baobab.desktop&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;ini&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;MimeType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;inode/directory&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With no user-level default set, Baobab won:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;$ xdg-mime query default inode/directory
org.gnome.baobab.desktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So &lt;code&gt;xdg-open&lt;/code&gt; launched Baobab, which expects a folder to scan, not to browse, and rejected
the &lt;code&gt;&amp;quot;.&amp;quot;&lt;/code&gt; argument with the error above.&lt;/p&gt;
&lt;p&gt;This happened in an LXQt session. Our guess (not tested elsewhere) is that it is most likely
on non-GNOME desktops, where nothing else sets a user default for directories, so Baobab&#x27;s
system-wide claim is the one that gets picked.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Make your file manager the user-level default for &lt;code&gt;inode/directory&lt;/code&gt; in
&lt;code&gt;~/.config/mimeapps.list&lt;/code&gt;. With pcmanfm-qt (LXQt&#x27;s file manager):&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;ini&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;[Default Applications]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;inode/directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pcmanfm-qt.desktop&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[Added Associations]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;inode/directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pcmanfm-qt.desktop&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or with &lt;code&gt;xdg-mime&lt;/code&gt;, which writes the &lt;code&gt;[Default Applications]&lt;/code&gt; entry for you:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;xdg-mime&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;default&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pcmanfm-qt.desktop&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;inode/directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use the desktop file of whatever file manager you have (&lt;code&gt;thunar.desktop&lt;/code&gt;,
&lt;code&gt;org.gnome.Nautilus.desktop&lt;/code&gt;, &lt;code&gt;pcmanfm.desktop&lt;/code&gt;, …).&lt;/p&gt;
&lt;p&gt;Verify:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;$ xdg-mime query default inode/directory
pcmanfm-qt.desktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No restart was needed; the next &amp;quot;Show in folder&amp;quot; click opened the file manager.&lt;/p&gt;
&lt;p&gt;The fix used on the affected machine was the &lt;code&gt;mimeapps.list&lt;/code&gt; edit; the &lt;code&gt;xdg-mime default&lt;/code&gt;
one-liner is the standard equivalent but was not the command actually run.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Chromium over ssh -X: Connection rejected because of wrong authentification</title>
    <link href="https://www.misc2.com/technotes/chromium-snap-ssh-x-wrong-authentification/"/>
    <id>https://www.misc2.com/technotes/chromium-snap-ssh-x-wrong-authentification/</id>
    <published>2022-09-02T00:00:00Z</published>
    <updated>2022-09-02T00:00:00Z</updated>
    <summary>Forwarded X11 worked for xterm but the snap-packaged Chromium failed with &quot;Connection rejected because of wrong authentification&quot;. The snap looks for .Xauthority inside its own home; symlinking it there fixes it.</summary>
    <category term="x11"/><category term="chromium"/><category term="snap"/><category term="ssh"/><category term="ubuntu"/><category term="xauthority"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Between two Ubuntu 20.04 machines, &lt;code&gt;ssh -X&lt;/code&gt; forwarding worked for &lt;code&gt;xterm&lt;/code&gt; and friends, but
Chromium refused to open:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;Connection rejected because of wrong authentification
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chromium on Ubuntu is a &lt;strong&gt;snap&lt;/strong&gt;. Snaps get their own &lt;code&gt;$HOME&lt;/code&gt; under &lt;code&gt;~/snap/&amp;lt;name&amp;gt;/current&lt;/code&gt;,
so Chromium looks for &lt;code&gt;.Xauthority&lt;/code&gt; there and never finds the cookie that sshd wrote to
&lt;code&gt;~/.Xauthority&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Link the real file into the snap&#x27;s home:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;ln&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-s&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;~/.Xauthority&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;~/snap/chromium/current/.Xauthority
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively run it with &lt;code&gt;XAUTHORITY=$HOME/.Xauthority chromium&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>FreeDOS 1.3 under QEMU on Windows 10: networking and mTCP FTP</title>
    <link href="https://www.misc2.com/technotes/freedos-1-3-qemu-windows-networking-mtcp-ftp/"/>
    <id>https://www.misc2.com/technotes/freedos-1-3-qemu-windows-networking-mtcp-ftp/</id>
    <published>2022-02-23T00:00:00Z</published>
    <updated>2022-02-23T00:00:00Z</updated>
    <summary>FreeDOS 1.3 in QEMU refused to bring up networking (&quot;QEMU network detected… Physical hardware networking is not supported&quot;). Removing one guard line from FDNET.BAT and using a pcnet NIC fixed it; an FTP 530 error was a Windows folder permission.</summary>
    <category term="freedos"/><category term="qemu"/><category term="mtcp"/><category term="ftp"/><category term="windows"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Networking did not come up at boot. FreeDOS printed:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;QEMU network detected.
Physical hardware networking is not supported at this time.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Later, mTCP&#x27;s &lt;code&gt;FTP.EXE&lt;/code&gt; could log in to an FTP server on the Windows host but then failed
with &lt;code&gt;530 User cannot log in, home directory inaccessible&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;FreeDOS 1.3&#x27;s network startup script (&lt;code&gt;FDNET.BAT&lt;/code&gt;) skips driver loading when it detects
QEMU. The 530 error came from the Windows FTP server: the anonymous user could not read the
shared folder.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Launch QEMU with an emulated AMD PCnet NIC and user-mode networking:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bat&lt;/span&gt;&lt;code&gt;qemu-system-x86_64 -m 150 -cpu 486 -rtc base=localtime &lt;span class=&quot;se&quot;&gt;^&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt; &lt;/span&gt; -drive file=freedos.img,format=raw -net nic,model=pcnet -net user
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;C:\FREEDOS\BIN\FDNET.BAT&lt;/code&gt;, comment out (or delete) the line that jumps past QEMU:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bat&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;REM if /i &quot;%1&quot; == &quot;start&quot; goto NoStartQEM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check &lt;code&gt;C:\FREEDOS\MTCP.CFG&lt;/code&gt; for sane settings (DHCP works with &lt;code&gt;-net user&lt;/code&gt;). After a reboot
the interface comes up.&lt;/p&gt;
&lt;p&gt;For the FTP error, open the shared folder&#x27;s &lt;strong&gt;Properties → Security&lt;/strong&gt; on the Windows host
and add the group &lt;strong&gt;Everyone&lt;/strong&gt; with read/write access (or whatever account the FTP site maps
anonymous users to).&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ESPHome ESP8266 nodes stop joining Wi-Fi: reason=&#x27;Association Leave&#x27;</title>
    <link href="https://www.misc2.com/technotes/esphome-esp8266-wifi-association-leave/"/>
    <id>https://www.misc2.com/technotes/esphome-esp8266-wifi-association-leave/</id>
    <published>2021-04-12T00:00:00Z</published>
    <updated>2021-04-12T00:00:00Z</updated>
    <summary>ESPHome NodeMCU devices that had been working suddenly could not connect, logging lwIP error -16 and &quot;Disconnected … reason=&#x27;Association Leave&#x27;&quot;. The router&#x27;s DHCP pool was full; giving the nodes static IPs fixed it.</summary>
    <category term="esphome"/><category term="esp8266"/><category term="nodemcu"/><category term="wifi"/><category term="dhcp"/><category term="iot"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ESPHome nodes that had worked for months stopped joining the network. The logs repeated:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;[I][wifi:194]: WiFi Connecting to &#x27;xyz&#x27;...
[W][wifi_esp8266:191]: wifi_apply_hostname_(somehostname): lwIP error -16 on interface st (index 0)
[W][wifi_esp8266:395]: Event: Disconnected ssid=&#x27;xyz&#x27; bssid=C8:0E:15:xx:xx:xx reason=&#x27;Association Leave&#x27;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The router&#x27;s &lt;strong&gt;DHCP address pool was exhausted&lt;/strong&gt;. The node associates, fails to get an
address, and the connection is torn down with &lt;code&gt;Association Leave&lt;/code&gt;, which looks like a Wi-Fi
problem but is really an IP-addressing one.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Either enlarge the DHCP pool or remove stale leases on the router, or give each node a
static address outside the pool in its &lt;code&gt;wifi:&lt;/code&gt; block:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;yaml&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;wifi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;xyz&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-wifi-password&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;manual_ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;static_ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;192.168.1.150&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;gateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;192.168.1.1&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;subnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;255.255.255.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pick an address outside the router&#x27;s DHCP range so it can never be handed out twice.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>VcXsrv: Authorization required, but no authorization protocol specified</title>
    <link href="https://www.misc2.com/technotes/vcxsrv-authorization-required-no-protocol/"/>
    <id>https://www.misc2.com/technotes/vcxsrv-authorization-required-no-protocol/</id>
    <published>2021-04-11T00:00:00Z</published>
    <updated>2021-04-11T00:00:00Z</updated>
    <summary>X11 apps from WSL refused to open on a VcXsrv display with &quot;Authorization required, but no authorization protocol specified&quot;. Relaunching VcXsrv with &quot;Disable access control&quot; ticked fixed it.</summary>
    <category term="x11"/><category term="windows"/><category term="wsl"/><category term="vcxsrv"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Starting an X11 program from WSL2 against a VcXsrv X server on the Windows side failed with:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;Authorization required, but no authorization protocol specified
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;VcXsrv&#x27;s access control rejects the connection, because WSL2 connects over a virtual network
and does not have a matching Xauthority cookie.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Quit VcXsrv and start it again with &lt;strong&gt;XLaunch&lt;/strong&gt;. On the &lt;em&gt;Extra settings&lt;/em&gt; page tick
&lt;strong&gt;Disable access control&lt;/strong&gt;. Programs then connect normally.&lt;/p&gt;
&lt;p&gt;This lets anything that can reach the port open windows on your display. Keep the Windows
firewall rule for VcXsrv limited to private networks.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>DHT22 temperature/humidity sensor on a NodeMCU 0.9 with ESPHome and Home Assistant</title>
    <link href="https://www.misc2.com/technotes/esphome-nodemcu-0-9-dht22-home-assistant/"/>
    <id>https://www.misc2.com/technotes/esphome-nodemcu-0-9-dht22-home-assistant/</id>
    <published>2021-04-11T00:00:00Z</published>
    <updated>2021-04-11T00:00:00Z</updated>
    <summary>Wiring and ESPHome YAML for a DHT22 on an old yellow NodeMCU 0.9 board, feeding Home Assistant. USB uploads failing with &quot;Upload with baud rate 460800 failed&quot; were fixed by disconnecting the sensor while flashing.</summary>
    <category term="esphome"/><category term="home-assistant"/><category term="esp8266"/><category term="nodemcu"/><category term="dht22"/><category term="iot"/>
    <content type="html">&lt;p&gt;&lt;img src=&quot;nodemcu-0-9.png&quot; alt=&quot;Yellow NodeMCU 0.9 board&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup &lt;a class=&quot;header-anchor&quot; href=&quot;#setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Home Assistant&#x27;s &lt;strong&gt;ESPHome&lt;/strong&gt; tab create a new node. For the device type pick
&lt;strong&gt;Other ESP8266s → NodeMCU 0.9&lt;/strong&gt;, add Wi-Fi credentials, then edit the YAML. The
&lt;a href=&quot;https://esphome.io/components/sensor/dht.html&quot;&gt;DHT sensor docs&lt;/a&gt; list the options.&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;yaml&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;esphome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;node-mcu-09&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;esp8266&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;nodemcu&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;wifi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-ssid&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your-wifi-password&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                              &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# fallback hotspot if Wi-Fi fails&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Node&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Mcu&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;09&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Fallback&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Hotspot&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fallback-password&quot;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;captive_portal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ota&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;esphome&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;sensor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;dht&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;DHT22&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;D2&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Temperature&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;humidity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Humidity&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;update_interval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;60s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Written in 2021 with &lt;code&gt;platform: ESP8266&lt;/code&gt; under &lt;code&gt;esphome:&lt;/code&gt;; current ESPHome wants the
separate &lt;code&gt;esp8266:&lt;/code&gt; block shown above.)&lt;/p&gt;
&lt;p&gt;Wiring:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;3.3V -&amp;gt; DHT22 VCC
D2   -&amp;gt; DHT22 DATA
GND  -&amp;gt; DHT22 GND
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The DHT22 datasheet recommends a &lt;strong&gt;4.7 kΩ pull-up&lt;/strong&gt; between VCC and DATA. Bare sensors need
one; most breakout boards already have it.&lt;/p&gt;
&lt;h2 id=&quot;symptoms-upload-over-usb-fails&quot;&gt;Symptoms: upload over USB fails &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms-upload-over-usb-fails&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Re-flashing over USB later failed every time with:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;text&lt;/span&gt;&lt;code&gt;Upload with baud rate 460800 failed. Trying again with baud rate 115200.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same happened with other NodeMCU boards.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Disconnect the sensor wires from the board&#x27;s pins while flashing, then reconnect them.
Something on the attached pins was getting in the way of the boot/flash sequence (D2 itself
is not a strapping pin, so the exact cause is unproven). Once the first image is on the
board, OTA updates over Wi-Fi avoid the problem.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Testing SD cards for fake capacity with f3 under WSL2</title>
    <link href="https://www.misc2.com/technotes/wsl2-test-sd-card-f3/"/>
    <id>https://www.misc2.com/technotes/wsl2-test-sd-card-f3/</id>
    <published>2020-04-27T00:00:00Z</published>
    <updated>2020-04-27T00:00:00Z</updated>
    <summary>How to mount a Windows drive letter (an SD card) in WSL2 with drvfs and run f3write/f3read on it to detect counterfeit, under-capacity cards.</summary>
    <category term="wsl"/><category term="sd-card"/><category term="f3"/><category term="windows"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You want to check whether an SD card really has the capacity it claims (fakes report a large
size but silently lose data past the real capacity), and the only Linux you have is WSL2.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mount the card&#x27;s drive letter (here &lt;code&gt;E:&lt;/code&gt;) with &lt;code&gt;drvfs&lt;/code&gt;, then run
&lt;a href=&quot;https://github.com/AltraMayor/f3&quot;&gt;f3&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;hl&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;bash&lt;/span&gt;&lt;code&gt;sudo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;apt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;f3
sudo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;mkdir&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-p&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/mnt/e
sudo&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;mount&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-t&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;drvfs&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;E:&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/mnt/e
f3write&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/mnt/e
f3read&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;/mnt/e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;f3write&lt;/code&gt; fills the free space with test files; &lt;code&gt;f3read&lt;/code&gt; reads them back and reports corrupt
or overwritten sectors. A genuine card shows no losses. See the
&lt;a href=&quot;https://linuxreviews.org/HOWTO_test_SD_cards_and_identify_fake_ones_(mostly_sold_on_ebay)&quot;&gt;LinuxReviews guide&lt;/a&gt;
for more background.&lt;/p&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Not a bug, just the missing step: WSL2 does not auto-mount removable drives, so you have to
mount the letter yourself with &lt;code&gt;-t drvfs&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using a Toshiba FlashAir W-02 Wi-Fi SD card after support ended</title>
    <link href="https://www.misc2.com/technotes/toshiba-flashair-w-02-without-setup-software/"/>
    <id>https://www.misc2.com/technotes/toshiba-flashair-w-02-without-setup-software/</id>
    <published>2020-04-12T00:00:00Z</published>
    <updated>2020-04-12T00:00:00Z</updated>
    <summary>Toshiba/KIOXIA dropped the FlashAir W-02 setup software in January 2020, but the card still works. Join its Wi-Fi network and browse to 192.168.0.1 to see and download the card&#x27;s files.</summary>
    <category term="flashair"/><category term="sd-card"/><category term="hardware"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;KIOXIA (formerly Toshiba Memory) ended support and removed setup software for the
FlashAir™ W-02 and Class 6 cards:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;technical support for and some software for wireless SD card FlashAir™ W-02 Class10 and
FlashAir™ Class6 … will no longer be available after 31 January 2020.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So there is no official way to configure or use the card.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The card&#x27;s built-in web server still works without the app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Insert the card in a camera (or reader) and power it so the card starts its access point.&lt;/li&gt;
&lt;li&gt;Join the card&#x27;s Wi-Fi network from a phone or laptop.&lt;/li&gt;
&lt;li&gt;The network properties show the DNS server / gateway as &lt;strong&gt;192.168.0.1&lt;/strong&gt;. Open
&lt;code&gt;http://192.168.0.1/&lt;/code&gt; in a browser.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You get a file listing of the card and can download files. There is no drag-and-drop upload,
but for pulling photos off a camera it is enough.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Panasonic Lumix GF1 (Japan model) — enable English menus with PTool</title>
    <link href="https://www.misc2.com/technotes/panasonic-gf1-japan-firmware-ptool-english/"/>
    <id>https://www.misc2.com/technotes/panasonic-gf1-japan-firmware-ptool-english/</id>
    <published>2020-04-12T00:00:00Z</published>
    <updated>2020-12-28T00:00:00Z</updated>
    <summary>A Japan-market Lumix GF1 can be given all interface languages by patching the official firmware with PTool. The steps that matter are the version increment of 10 and naming the file with a higher version.</summary>
    <category term="cameras"/><category term="panasonic"/><category term="lumix-gf1"/><category term="firmware"/><category term="hack"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Japan-market GF1 bodies only offer Japanese menus. Earlier attempts to flash a PTool-patched
firmware failed: the camera did not accept the update.&lt;/p&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The patched file must look like a &lt;em&gt;newer&lt;/em&gt; firmware. With the default version increment the
camera refused it. The clue came from JMF on
&lt;a href=&quot;https://www.personal-view.com/talks/discussion/117/gh1-firmware-hack-faq/p2&quot;&gt;personal-view.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Download PTool (originally &lt;code&gt;http://www.gh1-hack.info/ptool3d.zip&lt;/code&gt;; the site is gone, look
for a mirror), unzip and run it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Load Firmware&lt;/strong&gt;: load the official GF1 firmware from Panasonic&#x27;s site (here &lt;code&gt;GF1__122.bin&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Apply the patch: &lt;strong&gt;Patches for end users → Interface → All interface languages&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Tick &lt;strong&gt;version increment&lt;/strong&gt;, &lt;strong&gt;prevent version compare&lt;/strong&gt; and &lt;strong&gt;new body&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set the increment to &lt;strong&gt;10&lt;/strong&gt;, not 1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save Firmware&lt;/strong&gt; with the same name pattern but a higher version, here &lt;code&gt;GF1__123.bin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy it to the root of an SD card formatted &lt;strong&gt;in the camera&lt;/strong&gt;. Make sure it is the only
firmware file on the card.&lt;/li&gt;
&lt;li&gt;Fully charge the battery. Camera off, insert card, camera on, press the green
&lt;strong&gt;Playback&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Yes&lt;/strong&gt; on the &lt;em&gt;Version Up&lt;/em&gt; screen and press &lt;strong&gt;OK&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Afterwards the language menu lists English (and the other languages).&lt;/p&gt;
&lt;p&gt;Flashing modified firmware can brick a camera. Do it at your own risk.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>HP Officejet Pro 8600 &quot;Scan to Network Folder&quot; always fails on Windows 10</title>
    <link href="https://www.misc2.com/technotes/hp-officejet-8600-scan-to-network-folder/"/>
    <id>https://www.misc2.com/technotes/hp-officejet-8600-scan-to-network-folder/</id>
    <published>2020-04-10T00:00:00Z</published>
    <updated>2020-04-10T00:00:00Z</updated>
    <summary>Setting up Scan to Network Folder on an HP Officejet Pro 8600 always ended in an error, because the printer only speaks SMB1. Enabling the Windows SMB 1.0/CIFS Server feature made it work, at a security cost.</summary>
    <category term="printers"/><category term="hp"/><category term="smb"/><category term="windows"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;Scan to Network Folder&lt;/em&gt; setup wizard on an HP Officejet Pro 8600 always ended with a
generic error when pointed at a share on a Windows 10 PC.&lt;/p&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The 8600&#x27;s firmware only supports &lt;strong&gt;SMB 1.0&lt;/strong&gt; for network folders. Windows 10 ships with the
SMB1 server disabled.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Found in &lt;a href=&quot;https://h30434.www3.hp.com/t5/Scanning-Faxing-and-Copying/Setting-up-Scan-to-a-Network-Folder-on-HP-Officejet-Pro-8600/td-p/6484356&quot;&gt;this HP community thread&lt;/a&gt;:
enable the SMB1 server and reboot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Control Panel → Programs and Features → Turn Windows features on or off →
SMB 1.0/CIFS File Sharing Support → tick &lt;em&gt;SMB 1.0/CIFS Server&lt;/em&gt;&lt;/strong&gt; → OK, then reboot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;smb1-feature.png&quot; alt=&quot;Windows Features dialog with SMB 1.0/CIFS Server ticked&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution (2026):&lt;/strong&gt; SMB1 is obsolete and was the vector for WannaCry. Only enable it on a
trusted network, ideally on a machine whose only job is receiving scans. Scan-to-email or
HP&#x27;s own scanning software avoid SMB entirely.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  <entry>
    <title>Inkscape: Path → Trace Bitmap does nothing (no preview, no error)</title>
    <link href="https://www.misc2.com/technotes/inkscape-trace-bitmap-does-nothing/"/>
    <id>https://www.misc2.com/technotes/inkscape-trace-bitmap-does-nothing/</id>
    <published>2020-02-13T00:00:00Z</published>
    <updated>2020-02-13T00:00:00Z</updated>
    <summary>Trace Bitmap silently did nothing on an image already embedded in an Inkscape 0.92 document. Opening the image fresh in its own window, tracing there, and pasting the path back worked.</summary>
    <category term="inkscape"/><category term="linux"/><category term="ubuntu"/>
    <content type="html">&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms &lt;a class=&quot;header-anchor&quot; href=&quot;#symptoms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Selecting a simple black-on-transparent bitmap that was already embedded in a document and
running &lt;strong&gt;Path → Trace Bitmap…&lt;/strong&gt; did nothing: no preview, no error message, no outline.
Plenty of people online reported the same, without a fix.&lt;/p&gt;
&lt;h2 id=&quot;cause&quot;&gt;Cause &lt;a class=&quot;header-anchor&quot; href=&quot;#cause&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unknown. It seems to depend on how the image got into the document; a freshly opened copy
traces fine.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;header-anchor&quot; href=&quot;#fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Trace the image in a separate document and paste the result back:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Delete the embedded copy of the image.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File → Open…&lt;/strong&gt; the image file in a new window. In the import dialog choose
&lt;em&gt;Image DPI: From file&lt;/em&gt; and &lt;em&gt;Image rendering mode: None (auto)&lt;/em&gt;, then &lt;strong&gt;OK&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the image.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path → Trace Bitmap…&lt;/strong&gt;, tick &lt;strong&gt;Brightness cutoff&lt;/strong&gt;, press &lt;strong&gt;Update&lt;/strong&gt; (the preview now
appears), then &lt;strong&gt;OK&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ctrl+C&lt;/strong&gt; the new path, switch to the original document, &lt;strong&gt;Ctrl+V&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Resize with &lt;strong&gt;Ctrl&lt;/strong&gt; + a corner handle to keep proportions.&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
</feed>
