开发者
완전자동매매 시스템에 사람이 직접 개입해야 했던 사례 3가지
자동으로 돌아가게 만든 것과, 자동으로 끝까지 처리되는 것은 다른 문장이었습니다 이 시스템은 사람 승인 없이 스스로 판단하고 매매하는 걸 목표로 설계했습니다. 실계좌 주문 실행과 안전장치 (새 창)도 그 목표에 맞춰 만들었습니다. 그런데 최근 한 달 사이 실계좌에서 세 번, 사람이 직접 개입해야 하는 상황이 있었습니다. 세 사례 모두 "왜 자동 로직이 이 상황을 못 넘겼는지"의 구조가 서로 달랐습니다. 1. 배분 규칙이 특정 주문을 구조적으로 굶겼다 특정 종목 하나가 여러 날째 매도 계획이 서 있는데도 계속 팔리지 않는 걸 발견했습니다. 시스템은 매일 이 종목을 매도 후보로 올렸지만, 실제 주문까지는 못 갔습니다. 원인은 하루 매매 한도를 여러 라운드에 나눠 배분하는 규칙이었습니다. 이 종목의 주문 금액이 그날 남은 매도 한도보다 항상 컸습니다. 라운드 순서를 아무리 바꿔도 통과할 수 없는 구조였습니다. 한도 자체는 정상 작동하고 있었습니다. 문제는 "이번엔 못 나가도 다음 기회에 나간다"는 전제가 이 종목엔 애초에 성립하지 않았다는 점입니다. 잔여 한도가 매번 주문 금액보다 작으면, 기회는 계속 오지만 한 번도 충분하지 않습니다. 당장 못 나간 주문 1건은 사람이 직접 처리했습니다. 실계좌에서 이뤄진 되돌릴 수 없는 매도였습니다. 이후 배분 규칙 자체를 손봐서 같은 구조로 다시 굶는 일이 없도록 정리했습니다. 2. 안전장치가 스냅샷과 누적치를 혼동했다 다른 날엔 반대 방향의 사고가 있었습니다. 누적 손실을 감지하는 안전장치가 정상적인 매수 2건을 잘못 차단했습니다. 지수는 그날 거의 보합이었는데, 이 안전장치가 재는 손실률은 훨씬 크게 찍혀 있었습니다. 원인을 보니 이 장치는 "고점 대비 누적 하락"을 감지하는 용도였는데, 정작 비교하는 현재값은 장중 순간 스냅샷이었습니다. 장중 잠깐의 변동이 누적 지표를 밀어 올려서, 실제로는 발동하면 안 될 상황에서 발동한 겁니다. 누적을 재는 장치와 순간을 재는 장치가 뒤섞여 있었던 셈입니다. 막힌 매수 2건은 사람이 판단해서 직접 집행했습니다. 이후 이 안전장치가 장중 순간값이 아니라 "그날 마감 대 전날 마감" 기준으로만 반응하도록 구조를 바꿨습니다. 장중 급락에는 이제 다른 안전장치가 대신 반응하도록 역할을 나눴습니다. 3. 개입 경로 자체가 "새로 사는 경우"를 몰랐다 두 번째 사례를 수습하는 과정에서 사고가 하나 더 있었습니다. 수동으로 낸 주문을 원장에 반영하는 도구를 썼는데, 반영이 안 되고 조용히 빠졌습니다. 이 도구는 사람이 손으로 낸 거래를 세 가지 경우 중 하나로 분류합니다. 기존 보유 종목을 판 경우, 기존 보유 종목을 더 산 경우, 그리고 시스템과 무관한 거래인 경우입니다. 그런데 이번 매수는 원장에 없던 새 종목을 사람이 처음 사들인 경우였습니다. 세 분류 중 어디에도 안 맞았고, 도구는 이걸 "시스템과 무관한 거래"로 잘못 넘겼습니다. 그 결과 실제로는 산 자산이 잠깐 원장 밖에 있는 것처럼 표시됐습니다. 이 도구는 애초에 사람 개입을 위해 만든 경로였습니다. 그런데 그 경로를 설계할 때, "사람이 아예 새로운 자리에 처음 진입하는 경우"는 상정하지 않았습니다. 개입 경로 자체가 개입의 한 형태를 놓치고 있었던 셈입니다. 순서(먼저 다른 매도를 부기하고, 그다음 이 매수를 부기)를 지켜서 바로 수습했고, 검증 결과 원장과 실계좌 잔고는 정확히 일치했습니다. 분류 로직에 이 경우를 추가하는 건 아직 남은 과제입니다. 세 사례를 묶어보면 셋 다 "자동으로 처리되게 만들었다"와 "실제로 끝까지 처리된다"가 다른 문장이라는 걸 보여줬습니다. 첫 번째는 규칙이 있었지만 그 규칙이 특정 입력에서 절대 통과할 수 없는 구조였습니다. 두 번째는 장치가 있었지만 재는 대상(순간 대 누적)이 설계 의도와 어긋나 있었습니다. 세 번째는 사람 개입을 위한 경로가 있었지만 그 경로 자체가 특정 개입 형태를 몰랐습니다. 세 가지 모두 "자동화가 이 케이스를 놓칠 수 있다"는 걸 사전에 안 게 아니라, 실제로 놓친 뒤에야 알았습니다. 일반화하면 완전자동을 목표로 설계할수록,
AI 资讯
3 Cases Where Fully Automated Trading Still Needed a Human
Making something run automatically and having it actually get handled to completion turned out to be two different sentences This is the English version of a post originally written in Korean for my algorithmic trading system devlog (new tab). I designed this system to judge and trade on its own, without needing human approval for each decision. The order execution and safety-guard layer (new tab) was built around that same goal. Over the past month, though, there were three separate moments where I had to step in and act directly on the live account. Each one failed for a structurally different reason. 1. An allocation rule structurally starved one order I noticed a particular ticker had a sell plan queued for several days running, yet it never actually went out. The system kept nominating it as a sell candidate every day, but the order never reached execution. The cause was the rule that splits the daily trading budget across multiple rounds. This position's order size was consistently larger than whatever sell budget remained that day. No matter how the rounds were reordered, it could never clear. The budget cap itself was working exactly as designed. The problem was that the underlying assumption — "if it doesn't clear this time, it'll clear next time" — never held for this position. New opportunities kept arriving, but none of them was ever big enough. I executed the one blocked order by hand. It was an irreversible sell on the live account. Afterward, I reworked the allocation rule itself so the same starvation pattern couldn't recur. 2. A safety guard confused a snapshot with a cumulative reading On a different day, the opposite kind of failure happened. A guard meant to detect cumulative drawdown wrongly blocked two legitimate buy orders. The index was nearly flat that day, but the loss figure this guard was tracking read much larger. Looking closer, the guard was designed to measure "decline from peak," but the current value it compared against was an intra
开发者
개발일지 자동화가 한 달 가까이 멈춰 있었던 이유
로그조차 안 쌓이니, 돌고 있는지 죽어 있는지 구분할 방법이 없었습니다 이 블로그의 개발일지는 매일 밤 자동으로 마무리됩니다. 그날 대화로 초안을 썼으면 변환해서 로그에 남기고, 없으면 스킵했다는 한 줄만 남깁니다. 최근 이 파이프라인을 들여다볼 일이 있었는데, 7월 24일 이후로 로그가 통째로 비어 있었습니다. 무슨 일이 있었나 자동화 로그( .automation.log ) 마지막 줄이 2026-07-24였습니다. 그 뒤로 8월 22일까지, 거의 한 달 가까이 스킵 기록조차 한 줄도 없었습니다. 자동화가 아예 안 돌았나 싶어서 실행 로그( cron_output.log )를 열어봤습니다. 그런데 거기엔 매일 밤 실행된 흔적이 빼곡했습니다. 날짜 확인하고, 초안 있으면 내용 정리하고, 크로스링크까지 챙긴 요약이 매일 밤 남아 있었습니다. 일은 하고 있었는데, 결과물만 하나도 남지 않고 있었던 겁니다. 왜 아무도 몰랐나 실행 로그를 읽어보니 원인은 매일 같았습니다. 파일 쓰기 권한 승인을 기다리다가 그대로 끝난 겁니다. "workspace has not been trusted"라는 경고가 매 실행마다 찍혀 있었습니다. 초안을 잘 정리해놓고도, 마지막 파일 쓰기 한 줄이 승인 대기에 막혀서 아무것도 저장되지 않은 채 세션이 끝나는 패턴이 한 달 가까이 반복됐습니다. 문제는 이게 하필 로그를 남기는 단계 자체가 막힌 상황 이었다는 겁니다. 스킵한 날엔 스킵했다는 한 줄도 못 남겼습니다. 그러니 로그만 보면 "자동화가 멈췄다"와 "쓸 게 없어서 조용했다"를 구분할 수가 없었습니다. 무인 자동화이니 매일 밤 누가 화면을 지켜보는 것도 아닙니다. 결과적으로 이 공백은 사람이 우연히 로그 파일을 열어보기 전까지는 발견될 방법이 없는 구조였습니다. 진짜 원인 원인은 신뢰(trust) 설정이었습니다. 이 헤드리스 자동화 세션은 프로젝트 폴더 단위로 파일 쓰기 권한을 신뢰받아야 동작하는데, 그 신뢰 설정 키가 이 블로그 폴더가 아니라 상위 디렉터리(프로젝트들이 모여 있는 루트) 단위로 걸려 있었습니다. 즉 이 블로그 폴더만 놓고 보면 "아직 한 번도 대화형으로 신뢰 승인을 받은 적 없는 새 작업공간" 취급을 받고 있었던 셈입니다. 설정 파일 안에 이미 허용 규칙 10개가 들어 있었는데도, 그 규칙들이 걸려 있는 범위 자체가 무시되고 있었습니다. 헤드리스로 도는 야간 자동화는 대화형 승인 프롬프트에 응답할 사람이 없습니다. 범위가 어긋난 신뢰 설정 하나가, 매일 밤 정확히 같은 지점에서 조용히 실행을 무력화하고 있었던 겁니다. 흥미로운 디테일 하나 — 유령 완료 기록 이 공백을 되짚어보다가 특이한 줄 하나를 발견했습니다. 8월 22일 낮 12시 41분에 "weekly: 완료"라는 로그 한 줄이 남아 있었는데, 그 시각에 대응하는 실제 산출물 파일은 없었습니다. 같은 날 오후 4시에 다시 수동으로 실행된 기록이 있었고, 이번엔 실제 파일까지 정상적으로 만들어졌습니다. 앞선 12시 41분 기록이 왜 실물 없이 "완료"라고만 남았는지는 원인을 특정하지 못했습니다. 권한 문제가 한창이던 구간이라 어떤 형태로든 쓰기 절차 일부만 성공하고 일부는 실패한 걸로 추정만 할 뿐입니다. 다만 이 한 줄은 별도로 눈에 띄는 교훈을 남겼습니다. "완료"라고 적힌 로그도 그 자체로 완전히 믿을 수는 없다 는 겁니다. 로그와 실제 산출물을 따로 대조하지 않았다면 이 유령 기록을 그냥 지나쳤을 겁니다. 어떻게 고쳤나 신뢰 설정을 이 블로그 폴더 기준으로 다시 걸어주니, 그날 밤부터 바로 정상화됐습니다. 별도의 복잡한 조치는 필요 없었습니다. 문제는 고치는 방법이 아니라, 한 달 가까이 그 문제를 놓치고 있었다는 사실 쪽이었습니다. 일반화된 교훈 이번 일로 다시 확인한 건, 무인 자동화에서 "로그가 없다"는 상태 자체가 하나의 신호라는 겁니다. 그런데 그 신호를 신호로 취급하려면, 애초에 "침묵"과 "성공적인 무동작"을 구분할 수 있게 설계돼 있어야 합니다. 이번 파이프라인은 스킵한 날에도 로그 한 줄을 남기게 되어 있었습니다. 그 설계 덕분에, "로그가 아예 안
AI 资讯
The Bug That Hid Behind Its Own Comment: Fixing Inconsistent Inference in astroid
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . Project Overview astroid is the static-analysis engine that powers pylint — one of the most widely used linters in the Python ecosystem. Instead of running your code, astroid builds a model of what your code would do (a process called "inference") so pylint can catch real bugs before you ever hit run. That means astroid's inference logic has to be extremely consistent: if it gets confused about what a piece of code returns, pylint either misses real bugs, or — as in this case — flags perfectly correct code as broken. Bug Fix or Performance Improvement I picked up astroid issue #3077 : identical typing.cast(T, self) expressions were being inferred differently depending only on how the surrounding call was written — even when the code was structurally symmetric. In a class like this: class Base : def __call__ ( self ) -> str : return cast ( str , self ) def run ( self ) -> str : return cast ( str , self ) class IrJoin : separator : Base def __call__ ( self , items ): sep : str = self . separator () # implicit __call__ sugar return sep . join ( items ) def run ( self , items ): sep : str = self . separator . run () # explicit method call return sep . join ( items ) Both self.separator() and self.separator.run() do the exact same thing at runtime — I verified this by actually running the file. But pylint only flagged one of them: $ python -m pylint t5.py t5.py:35:15: E1101: Instance of 'Base' has no 'join' member (no-member) The explicit .run() path got a false positive; the equivalent implicit __call__ path did not, even though sep is a plain str in both cases at runtime. Code PR: https://github.com/pylint-dev/astroid/pull/3242 My Improvements Ruling out the obvious suspect My first hypothesis was infer_typing_cast , the function that handles typing.cast() itself — it seemed like the natural place for a cast-related inconsistency to live. Tested in isolation, though, it behaves identi
AI 资讯
Software Testing Interview Questions
1. What is a Test Case? A Test Case is a set of steps, test data, conditions and expected results used to check whether a particular functionality is working correctly or not. Example: For a login page, enter a valid username and password and click Login. The expected result is that the user should successfully log in. 2. What is a Test Scenario? A Test Scenario is a high-level functionality or condition that needs to be tested. Example: "Verify Login Functionality" is a Test Scenario. Under this scenario, we can create multiple test cases like valid login, invalid password, empty username, empty password, etc. 3. What are Negative Test Cases? Negative Test Cases are used to check how the application behaves when invalid or unexpected data is given. Example: Entering an incorrect password or leaving the username field empty. The application should not crash and should show the proper error message. 4. What are Positive Test Cases? Positive Test Cases check whether the application works correctly with valid and expected input. Example: Entering a valid username and password should allow the user to log in successfully. 5. Relationship Between Test Case and Test Scenario A Test Scenario is a high-level requirement or functionality, while a Test Case contains detailed steps to test that scenario. Example: Test Scenario: Verify Login Functionality. Test Cases: Login with valid username and password. Login with invalid password. Login with empty username. Login with empty password. So, one Test Scenario can have multiple Test Cases. 6. What is Unit Testing? Unit Testing is testing individual units or components of software separately. Usually, developers perform Unit Testing. Example: If there is a function that calculates the total price, we can test that function separately to check whether it returns the correct result. 7. What is Integration Testing? Integration Testing is used to check whether two or more modules work correctly after they are combined. It mainly foc
AI 资讯
The flaky test was right: a 58%-reproducible race in a scroll-reading pipeline's disk cache
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . Project Overview The Vesuvius Challenge uses machine learning to read carbonized Herculaneum scrolls, which is 2,000-year-old papyrus that got buried by the eruption of Vesuvius and can never be physically unrolled. Its open-source monorepo, ScrollPrize/villa, contains the vesuvius Python package that researchers use to stream multi-terabyte CT scan volumes and train ink-detection models. I was setting up that package on my Windows 11 machine (the project's CI only tests Ubuntu, and the workflow file literally says "Extend this list once the build scripts for macOS and Windows are confirmed"), working with an AI coding assistant to run the test suite on a platform it had never been tested on. One test failed. Then it passed. Then it failed again. Bug Fix or Performance Improvement The test, test_shared_cache_multiprocess_reads_are_not_torn, spawns four processes that read one scroll volume through a shared on-disk chunk cache. Run it once and you might not see anything wrong. So I ran it twelve times: 7 failures out of 12, all PermissionError: [WinError 5] Access is denied. A 58% flake isn't a flake. It's a bug with a coin flip attached. The cache is on the hot path for real usage. It's the component behind the package's documented volume_cache_dir config and the --cache-dir flag of its inference CLI. Any PyTorch DataLoader with num_workers > 0 puts multiple processes into exactly this concurrent pattern, so on Windows, training runs would randomly die mid-epoch. Once I dug in (a standalone reproducer that propagated full worker tracebacks instead of repr(exc)), the failure turned out to have three separate surfaces, each one hiding behind the previous one: Cache-entry commit. The zarr library commits each cache entry with a write-temp-then-os.replace pattern. On POSIX, rename(2) over a file another process has open is legal. On Windows, MoveFileEx(MOVEFILE_REPLACE_EXISTING) return
开发者
The Optimization That Was Too Good: Why Our Push Notifications Only Worked When You Weren't Looking
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. When I was...
AI 资讯
Fixing a null-body crash in the Formbricks survey SDK, found by Sentry
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . A survey widget should not be able to take down the page it is embedded on. This one could: a single API response with a null body threw an uncaught TypeError in the visitor's browser. Sentry's bot found it, filed it as a GitHub issue, and Seer pointed at the exact line. Here is the fix. Project Overview Formbricks is an open-source survey and experience-management platform. Websites and apps embed a small JavaScript SDK that loads a survey, shows it to a user, and posts the answers back to the Formbricks API. The SDK lives in the monorepo as two packages: @formbricks/js-core (the loader and command queue) and @formbricks/surveys (the survey renderer). Both talk to the backend through a shared makeRequest helper. Bug Fix or Performance Improvement I fixed issue #6581 , a production crash that Sentry filed automatically: Bug: API data is not always validated in the surveys package TypeError: Cannot read properties of null (reading 'data') The issue was opened automatically by sentry[bot] , and its body carries the Sentry-captured (minified) stack trace plus a link to the source event, FORMBRICKS-CLOUD-3VE . Sentry did not just record this crash, it reported it. The SDK calls makeRequest to load a workspace's environment state. That code parsed the HTTP response and immediately read .data off the result: const json = ( await response . json ()) as ApiResponse ; // ... const successResponse = json as ApiSuccessResponse < T > ; return ok ( successResponse . data ); Two things go wrong here: response.json() on a body of literal null returns JavaScript null . Reading null.data throws TypeError: Cannot read properties of null (reading 'data') . The error path had the same problem one line up: errorResponse.code on a null body throws reading 'code' . response.json() is not guarded at all. A non-JSON body (an empty response, or an HTML error page from a proxy or CDN) makes it throw an unhan
AI 资讯
A Reason Code Without a Source Is Half a Diagnostic
A failure message can be technically correct and still be frustratingly incomplete. Consider a timeout. It tells us something important about the failure mechanism, but not which operation encountered it. Adding the complete request target might answer that question, yet it can also expose identifiers, query parameters, access material, or other data that never belonged in a broadly visible diagnostic record. A safer middle ground is to give failures two separate coordinates: a reason code that explains how the operation failed, and a bounded operation label that explains where it failed. That distinction makes diagnostics more useful without turning failure handling into an accidental data-exposure channel. A reason code is not a location Reason codes describe failure mechanics. Generic examples might include deadline , cancelled , unauthorised , or invalid_response . These codes are valuable because they let systems group similar outcomes. A dashboard can count deadline failures across operations, while application logic can decide whether a particular reason is retryable. What a reason code cannot reliably explain is the operation being attempted. A deadline during a summary read may require a different investigation from a deadline while assembling a detailed response. Combining both meanings into one free-form message makes failures harder to query and encourages presentation text to become an informal data model. Model the two coordinates separately A deliberately generic, invented C# model might look like this: public enum OperationArea { Summary , Detail , Archive } public sealed record FailureDetail ( string ReasonCode , OperationArea ? Area = null ); The reason remains suitable for classification. The operation label adds location without carrying an unrestricted request value. An enum is not the only option. A validated value object or centrally managed set of constants can work too. The important constraint is that labels come from a small, reviewed voca
AI 资讯
My probe passed because it could not fail
Originally published on hexisteme notes . I run pre-registered checks against a live system, read the verdict, and move on — that's the whole point of pre-registering them, so I don't get to argue with the result after the fact. Most of the time the discipline pays for itself. This time it passed, and the pass was wrong, and the reason it was wrong is more interesting than the failure itself: the check could not have returned anything else, whatever had actually happened to the file under test. The question I was probing something narrow: does a hand-made audio crossfade survive a round trip through DaVinci Resolve? Build a timeline with a crossfade sitting on a cut, export it to FCPXML 1.10, re-import it, and see whether the crossfade is still there. Third-party documentation says transitions are invisible to and unmodifiable by the scripting API. Believing that, I pre-registered a judgment method that never looks at timeline structure at all: render audio around the splice and classify it by waveform shape. The judge, exactly as pre-registered: render two seconds either side of the cut, downsample to 8 kHz mono, compute a 20 ms sliding-window RMS envelope — 202 windows across the render — and take the largest normalized step between adjacent windows. Above 0.5, call it a hard cut: the fade is gone. Below 0.5, call it a gradual ramp: the fade survived. The probe came back pass — gradual ramp, max step 0.4761, under the 0.5 threshold. Exit 0, all green. The crossfade had actually been lost at the export step. The pass was a false confirm, and I only found that out by going back in with a second, read-only inspection after the fact. Why the check could not fail The prep instructions for this probe — which I also wrote — said the easiest way to get two adjacent audio items with enough handle to build a crossfade is to take one continuous clip and blade-split it in the middle. That's a completely reasonable instruction on its own. A crossfade needs overlap media on bot
开发者
One rented /24 could eclipse a Kademlia node. Now it takes ten.
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. The...
AI 资讯
The test was green. Every real connection would have failed.
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. The...
AI 资讯
The Smallest Fix With The Biggest Impact [Skips VS Technology Edition]
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . Remember that one Regular Show episode where Skips tried to destroy the park's computer because it caught the Error 220 bug? He took one look at it, picked up a sledgehammer and said the line we’ve all felt as devs: “ There’s something evil in that computer. We gotta smash it ”. In the cartoon, they literally smash the computer and this works to fix the bug. In real life? We don’t get sledgehammers. We get Github PRs. Last week, I almost felt like Skips. I found a one-line bug in an open source repo that could’ve broken Instagram webhook security. No hammer, no explosion, just one misindented ‘if’ statement and a missing test. This is the story of how the smallest fix had the biggest impact. -The Challenge So what was my Error 220 ? While contributing to the corsair open-source repo, I found a security breach in the Instagram webhook handler. Something about the verification flow felt off, so I started tracing it line by line. The code called timingSafeEqual but the result was indecisive. I took an extensive look at it and that's when I saw it- The if statement meant to guard the check was there, but timingSafeEqual was indented wrong. It was meant to return the result of timingSafeEqual to accept or reject the request, but it fell through instead. Although it was running, its return value wasn’t being used to control the flow. This bug was tiny-one mis-indented line- but it had a great impact. In JS, it is not considered an error and so it’s easy to miss. Webhook security relies on a signature check to prove a request. If timingSafeEqual isn’t actually enforcing it, an attacker could forge a webhook and it would be accepted. The entire protection could fall apart over one tab. View PR #759 -The Fix In fixing it, I opened PR#759 to correct the indentation so crypto.timingSafeEqual would be inside the if block and its boolean result would decide whether to return true or false . Prior
AI 资讯
Fix Next.js "params should be awaited" Error in Next.js 15+
Fix Next.js "params should be awaited" Error in Next.js 15+ If you are seeing the params should be awaited Next.js error after upgrading to Next.js 15 or following an older App Router tutorial, you are not alone. The error usually looks something like this: Route "/blog/[slug]" used params.slug. params should be awaited before using its properties. Sometimes it appears with searchParams . Sometimes it appears with cookies() or headers() . And sometimes the page still seems to work, but your terminal keeps shouting at you. This article will slow it down and explain the fix in a beginner-friendly way. No deep framework lecture first. Just the actual problem, the broken code, the fixed code, and the reason it works. What This Error Means in Plain English In older Next.js code, you may have treated params like a normal JavaScript object. Something like this: const slug = params . slug ; That used to feel natural. If your route was: /blog/[slug] and the user opened: /blog/my-first-post you expected: params . slug ; // "my-first-post" In newer Next.js versions, especially Next.js 15+, some request-based values became asynchronous. That means you should treat them like values that need to be waited for before you read from them. So instead of reading params.slug directly, you do this: const { slug } = await params ; That is the heart of the fix. The error is not saying your route is missing. It is not saying your [slug] folder is wrong. It is saying: You are trying to read route data before awaiting it. The common flow: the page loads, the code reads params.slug directly, Next.js expects params to be awaited, and the error appears. Why This Changed Next.js has a group of features called Dynamic APIs . That sounds more complicated than it is. In simple terms, Dynamic APIs are values that depend on the current request. For example: What route did the user open? What query string is in the URL? What cookies came with this request? What headers came with this request? Is draft
AI 资讯
The Login Loop of Doom.
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . Code snippets are recreated and anonymized for illustrative purposes. The Symptom: A Revolving Door Instead of a Login Page It started innocently enough: I was clicking through our app and hit "Log in." Auth0's Universal Login page appeared, I entered my credentials, got redirected back to the app... and landed on the Auth0 login page again. And again. And again. No error message. No failed login attempt. Auth0 was happily authenticating me every single time — and our app was just as happily bouncing me right back, like a bouncer who checks your ID, nods, and then immediately forgets he checked it. The login loop. Every developer's favorite horror movie, now starring me. Red Herring #1: "It's the Frontend's Fault" My first suspect was the obvious one: the frontend callback handler. A Node.js/Express app sits in front of our Django API, handling the Auth0 redirect dance. A login loop screams "broken callback" or "state/nonce mismatch," so I spent a solid hour there: ✅ State parameter matched ✅ Nonce validated ✅ Callback URL whitelisted in the Auth0 dashboard ✅ ID token and access token both present in the response Everything the frontend touched was perfect. The tokens were real, signed by Auth0, freshly issued seconds ago. And yet the moment the frontend sent the access token to our Django API, the API answered with a flat 401 Unauthorized . Fine. New suspect. Red Herring #2: "Auth0 Must Be Misconfigured" Next stop: the Auth0 dashboard. Maybe the token lifetime was set to something absurd, like 5 seconds? Maybe the audience claim was wrong? Token lifetime: 3600 seconds. Normal. aud claim: matched our API identifier exactly. Signature: verified against the JWKS. Valid. So Auth0 was issuing perfectly good tokens, the frontend was delivering them intact, and Django was spitting them out. The bug had to be in the validation logic itself. Time to actually read the code we trusted blindly e
AI 资讯
A Dead PID Held My Lock for 2 Hours: One Missing Line, Zero Output, exit 0 Every Time
For 30 straight days as a college student earning ¥100k/month, I posted to Instagram by hand, and then I burned out and stopped. Today the same job runs on a Claude Code autonomous environment, I touch nothing, and it holds up ¥1.2M/month in revenue. Except for the two hours when it quietly stopped: three consecutive launchd runs, zero pieces of content generated, last exit=0 every single time, and not one alert. The cause was a process that had already been killed, holding a lock file nobody would take away from it. Why this setup works From "doing the work" to "building the environment" The problem with updating social media by hand is that it burns willpower. No matter how motivated you are, sleep, health, and mood all fluctuate. During the period when I was laid off and my income went to zero, I had no mental slack for posting at all. The autonomous environment I spent six months building with Claude Code runs regardless of my emotional state. launchd calls a script, the script generates content with claude -p (MAX plan quota; paid APIs are off-limits), the output is queued for auto-posting, and it goes out to Instagram every day at 19:30. As long as this machinery keeps working, ¥1.2M/month in sales holds up without me lifting a finger. The mental model I want to hand you A lot of people think "automation = writing scripts," and that's only half right. A script is correct at the moment you write it. Given time, external dependencies break, processes die for reasons you didn't anticipate, and lock files turn into debris that blocks every future run. An autonomous environment that actually works is one that assumes breakage and carries a layer that repairs it. The lock story here is a textbook case. ~/dev/brand-404/sns/gen_feature.py is a script launched on a schedule by launchd that auto-generates Instagram feature articles. A single run takes a long time (up to three claude -p calls, plus image generation, adding up to tens of minutes), so it has a lock mechani
AI 资讯
# From Silent Failure to a Definitive Fix: Debugging an Existing AI Application
Clear the Lineup Submission The Bug AI applications can fail silently — producing wrong outputs, degraded performance, or unexpected behaviors without explicit errors. In my case, the issue was SQL drift: queries executed successfully but returned incomplete or unstable results due to unsafe wildcard usage (SELECT *). This silent failure propagated downstream, degrading model accuracy without obvious alerts. The Fix I introduced an agentic validation and inspection layer into the pipeline using LangGraph, StatesGraph, MCP, and A2A. Inspection Layer: Deterministic checks (SQL linters, schema validators). Validation Layer: Agentic reasoning about query safety. MCP Integration: Standardized access to profilers and monitoring APIs. A2A Collaboration: Agents exchanged context to enforce compliance. This combination allowed the system to detect unsafe queries and route them for human review before deployment. PR Link Here’s the merged PR where the fix was implemented: Continental-Thaligai Repository – Merged PRs https://github.com/NikhilRaman12/Continental-Thaligai/pulse#opened-pull-requests Code Snippet python from langgraph import Graph from statesgraph import State from mcp import MCPClient class SQLInspection(State): def run(self, query): if "SELECT" in query and "*" in query: return {"risk": 0.7, "message": "Wildcard SELECT may cause drift"} return {"risk": 0.1, "message": "Query safe"} graph = Graph() graph.add_state("sql_inspection", SQLInspection()) graph.connect("sql_inspection", "human_review", condition=lambda r: r["risk"] > 0.5) result = graph.run("SELECT * FROM transactions") print(result) Diff Example: diff SELECT * FROM transactions SELECT transaction_id, amount, date FROM transactions This change eliminated silent drift in query results and improved reliability in downstream AI pipelines. Outcome Silent SQL drift eliminated. Improved accuracy in downstream AI models. Added regression tests to prevent recurrence. Strengthened CI/CD pipeline with agentic saf
开发者
The same Rust gave two different answers, and neither matched JavaScript
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. Demo...
AI 资讯
The Ultimate IDOR Testing Checklist (2026 Edition)
Ultimate IDOR Testing Checklist Phase 1: Setup & Target Identification [ ] Create Test Accounts: Create two accounts (Attacker and Victim) for safe testing of destructive requests (POST/PUT/DELETE). [ ] API Identification: Find JSON endpoints over rendered HTML. [ ] Sensitivity Analysis: Target critical functions first (password reset, account recovery, financial data, DMs, user management). [ ] ID Audit: Check if endpoint is private or public and contains any kind of ID parameter. [ ] ID Leakage: Check for IDs leaked via other API endpoints or public pages (public profile pages, listings). [ ] Map Clients: Collect web/mobile clients, open APIs from decompiled mobile (jadx/apktool), and swagger/openapi if present. Phase 2: Direct ID Substitution & Enumeration Technique Scenario to Test (Attacker ID=10, Victim ID=9) Basic ID Flip GET /api/v5/users/10 -> GET /api/v5/users/9 Incremental Numeric Brute Force Loop over sequential numeric IDs (decrement/increment from own ID). Non-Numeric ID Substitution Replace param with email / username / UUID. Complex ID Brute Force Brute force short alphanumeric segments (last 1–4 chars). Predictable ID / Combined ID /user/2222/data/3333 — change one or both parts. Hashed/Derived IDs (MD5/SHA1 pattern) Detect hashed IDs, create accounts to infer mapping, try replacing derived hashes. Phase 3: Path and URL Manipulation Bypasses Technique Scenario to Test (Attacker ID=10, Victim ID=9) Trailing Slash GET /api/v5/users/9 -> GET /api/v5/users/9/ Double Slashes / Obfuscated Path GET /api/v5/users//9 or GET /api/v5/users/./9 Case Variation / Key Swapping /api/User?id=123 vs /api/user?id=123 or user_id ↔ userid Path Traversal / Mixed Paths POST /users/delete/my_id/../victim_id Wildcard Substitution GET /api/users/* or GET /api/users/user_id Fuzz Keywords in Path GET /api/v3/users/12345 -> /api/v3/users/all SQLi Quick Check GET /api/v3/users/12345' Phase 4: Logic & Endpoint Bypasses Technique Scenario to Test Version Downgrading GET /v3/user/1
开发者
analogous(-1): how a default hid a heap-exhaustion bug for fifteen years
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry. ...